Skip to main content

KERNEL.md

# KERNEL — five members (v1, frozen definitions)

Durable definitions only. Each is backed by an evidence report named at its foot; the
reports carry the version-specific numbers, this file carries the invariants.

A note on shape: **Delegation, Observation, and Channel are mechanisms** (things a
runtime/server does); **Cursor is a construct**; **Interaction Persistence is not a
mechanism — it is a substrate contract** that Delegation and Cursor each depend on.
The five are not five symmetric operations. Do not read Interaction Persistence as
something you "invoke."

---

## 1. Delegation (mechanism)

The server emits declarative markup that transfers ownership of a browser-native
interaction machine to the browser.

**Invariants:**

- Browser owns the native interaction state when delegation is **patch-safe**.
- **Delegated browser-owned state must not be stored in a server-reconciled
  attribute.** (State reflected into an attribute the server template does not carry
  is stripped by reconciliation.)
- **Structural attributes a browser machine requires must be server-rendered
  constants.** (A client-added structural attribute — e.g. `popover` — is stripped by
  reconciliation just like reflected state. The attribute must be static in the
  server template so the machine keeps its shell.)
- **popover is green.** **dialog and details are red** (see `RED_FIXTURES.md`).

*Evidence: `reports/CONFORMANCE_REPORT.md`, `reports/COMBOBOX_SPIKE_REPORT.md` (test 7).*

## 2. Observation (mechanism)

A declarative relay forwards browser-machine events to the server as ordinary
LiveView events.

**Invariants:**

- Observation is **lossy browser-to-server telemetry**.
- It is **never authoritative**.
- The server **may react** to observed events (lazy cleanup, logging, non-gating
  assigns) but **must not derive canonical state or authorization** from observed
  browser state.

The dialog-close-relay evidence shows the signal is lossy in three independent ways
(a reconciliation-stripped close fires no event; a disconnected close is dropped;
`<dialog>` has no open event at all), so the non-authoritative rule is not hygiene —
it is the only safe reading.

*Evidence: `../archive/observation-spike/OBSERVATION_SPIKE_REPORT.md`.*

## 3. Interaction Persistence (substrate contract — NOT a peer mechanism)

The condition under which owned interaction state survives a patch. It splits by
owner and must **not** be collapsed into a single "Identity" concept.

**Browser-owned tier:**
`DOM node continuity + state-surface non-interference.`
The same DOM node object must persist **and** the machine's state surface (attribute
or top-layer) must not be overwritten. Node continuity alone is insufficient (dialog
keeps its node and still loses `open`).

**Client-owned tier:**
`stable logical identity + explicit userland rebind/re-projection machinery.`
Not provided by LiveView. The two required mechanisms, both evidenced:

- **child-node replacement** → the container hook re-projects presentation by logical
  id in `updated()`;
- **hook-node (container) replacement** → recover state from **external storage keyed
  by the logical container id** on remount.

**Standing caveat (durable):** LiveView node continuity is **observed implementation
behavior, not a documented framework guarantee**. The harness exists to detect when it
changes.

*Evidence: `reports/CONFORMANCE_REPORT.md`, `reports/CURSOR_REBIND_SPIKE_REPORT.md`.*

## 4. Cursor (construct)

Client-owned coordination state over server-rendered, identity-bearing children.

**Invariants:**

- Primary strategy is **`aria-activedescendant`** (focus stays on a persistent
  input/container; the active item is a logical-id reference).
- **Roving tabindex is not patch-safe** as the default strategy — a focused non-form
  element is blurred when a patch touches its subtree (LiveView preserves focus for
  form inputs only).
- The active descendant **must always target a live node, or collapse
  deterministically** (clear/first/nearest, chosen and documented) — it must never
  point at a removed or stale node.
- Cursor is a specialization of the Interaction Persistence client tier; it inherits
  the "stable logical id + explicit rebind" requirement.

*Evidence: `reports/CURSOR_REBIND_SPIKE_REPORT.md`, `reports/COMBOBOX_SPIKE_REPORT.md` (tests 3–5).*

## 5. Channel (mechanism — a server obligation)

A versioned async query/result exchange between a client-owned input atom and
server-owned result content.

**Invariants:**

- **version-through-render**: the client stamps each query with a monotonically
  increasing version; the server stamps each result render with the version it answers.
- **server-enforced monotonicity is required for construction-grade correctness**:
  the server render path must **refuse to render superseded versions**. The package
  ships `LiveInteractionContracts.Channel` as the reference guard; an application must
  route result rendering through that guard to inherit the construction-grade claim.
- **client-side stale detection is a secondary guard, not the primary one.** The
  version stamp alone, without `Channel.accept/2` on the server render path, is
  **convention** — stale results still enter the DOM and are only flagged after the
  fact.
- Channel stays in the kernel because Combobox correctness is impossible without it,
  and the version must round-trip *through the server render* (coupling it to the
  pipeline in a way generic app-level versioning is not).

*Evidence: `reports/COMBOBOX_SPIKE_REPORT.md` (test 2 — guard on vs off).*

---

## Derivations (not kernel; for orientation only)

- **Dialog** = Delegation (red) + Observation. Red fixture.
- **Popover** = Delegation (green). Reference machine.
- **Dropdown menu** = Popover + Cursor.
- **Combobox** = Popover + Cursor + Channel. Proven to compose on one subtree.