Packages

A real-browser external-app verifier for patch-safe Phoenix LiveView interactions, with secondary unstyled reference components.

Current section

Files

Jump to

README.md

# live_interaction_contracts
Real-browser verification for interactions in your own Phoenix LiveView application.
Point the external-app contract runner at an already-started application to check
whether browser-native interaction state survives LiveView patches and to produce
machine-readable evidence. The package also ships a calibration harness, delegation
ledger, and unstyled reference components as secondary implementation references.
Use it when you need to know whether a popover, menu, tooltip, select, combobox,
dialog adapter, focus handoff, or async query shell is safe under LiveView DOM
patching.
## Why this exists
LiveView gives the server authority over rendered HTML, but many interaction
machines keep their state in the browser: top-layer popovers, active descendants,
focus, selection, pending input, and listbox shells.
The hard failures happen when both sides think they own the same state. A patch
replaces a node, a client hook rebinds, the server renders stale results, or the
browser keeps state that the server cannot observe.
`live_interaction_contracts` turns those cases into executable contracts. Each
green claim is backed by browser tests across Chromium, Firefox, and WebKit.
Amber and unknown cases stay explicit.
The shortest proof is the harness's **one patch, three outcomes** trial: the same
acknowledged diff leaves the same DOM node alive in a popover, a raw modal dialog,
and an `open`-protected modal dialog. The popover stays coherent, the raw dialog is
torn into `open=false` while `:modal` still matches, and the protected dialog stays
coherent while its server-rendered child still updates. The controlled comparison is
raw versus protected dialog; popover is the native-safe contrast.
## What ships
- `mix live_interaction_contracts.check`, a strict contract runner for an
already-started LiveView app, backed by Playwright and atomic JSON evidence.
- A Mix task that runs the conformance harness against real browsers.
- A generated delegation ledger that classifies each state machine as green,
amber, red, or unknown.
- Reference components for `<.popover>`, `<.tooltip>`, `<.menu>`, `<.select>`,
and `<.combobox>`.
- A `LiveInteractionContracts.Channel` helper for monotonic async query results.
- Contract documents that define the state ownership rules.
The components are intentionally unstyled. They are reference primitives, not a
finished UI kit. The future UI kit direction is Temper UI; this package is the
proof layer underneath it.
## Install
For harness, ledger, and CI use:
```elixir
{:live_interaction_contracts, "~> 1.2", only: [:dev, :test], runtime: false}
```
If you also use the reference components at runtime:
```elixir
{:live_interaction_contracts, "~> 1.2"}
```
The harness requires `elixir`, `node`, and Playwright browsers:
```sh
npm install --save-dev playwright@1.61.1
npx playwright install chromium firefox webkit
```
The reference components require Phoenix LiveView 1.1 or later. Their client JS
ships as Phoenix colocated hooks. After `mix compile`, register the extracted
hooks in your `app.js`:
```javascript
import {hooks as licHooks} from "phoenix-colocated/live_interaction_contracts";
new LiveSocket("/live", Socket, {hooks: {...hooks, ...licHooks}});
```
## Reference component example
```elixir
defmodule MyAppWeb.Demo do
use Phoenix.Component
use LiveInteractionContracts.Components
def demo(assigns) do
~H"""
<.popover id="demo" placement="bottom">
<:trigger>Open</:trigger>
<:content>Patch-safe by contract.</:content>
</.popover>
"""
end
end
```
## Run the harness
Run these commands from your Phoenix project:
```sh
# all stable browser engines
mix live_interaction_contracts.test
# one browser
mix live_interaction_contracts.test --browser chromium
# one suite
mix live_interaction_contracts.test --suite combobox
# early warning against LiveView main
mix live_interaction_contracts.test --lv "github:phoenixframework/phoenix_live_view#main"
# regenerate the delegation ledger
mix live_interaction_contracts.ledger
# advisory app audit for patch-safety and dual-write risk
mix live_interaction_contracts.audit --path /path/to/app
# compare two harness result sets
mix live_interaction_contracts.compare --a before.json --b after.json
```
By default, the harness tests against your project's LiveView version.
## Check your own LiveView
Skills and code review can predict ownership risks; this command verifies the real
result. Start your application, then point a strict v1 JSON contract at it:
```sh
mix live_interaction_contracts.check \
--url http://127.0.0.1:4000 \
--contract test/interaction_contracts/account_menu.json
```
The contract describes click, hover, or deterministic scroll-into-view activation;
a click or a restricted `Enter`/`Space` key press that causes a LiveView patch; a
subject-local attribute whose change proves the patch arrived; and before/after
expectations for DOM object identity, an exact attribute value (including `null` for
absence), popover, dialog, or focus state. A press trigger can patch while the pointer
remains over a hover-owned interaction: pressing does not move the pointer, and the
declared post-state proves whether the hover-owned state actually survived the patch.
Node identity, popover, and dialog probes may use optional unique selectors; omitted
selectors target the contract subject. Focus and attribute probes require unique
selectors. Only the acknowledgment selector is required to be subject-local; probe
selectors are not restricted to descendants. The runner writes
`live-interaction-contracts-result.json` and exits non-zero for failed,
inconclusive, or invalid runs. See `BLUEPRINT.md` and
`priv/harness/consumer-popover-contract.json` for the v1 format.
Targets are restricted to loopback by default because contract clicks may cause real
application side effects. `--allow-remote` exists only for an explicitly safe test
environment; do not point the runner at production.
### 1.x compatibility
Because this package is already published as 1.x, existing Mix tasks, component APIs,
v1 contract fields, result fields, and exit-code meanings are compatibility
constraints. New probes and optional evidence may be added within 1.x; removing or
reinterpreting existing fields requires a new contract/result schema and a package
major release. M1 hardening is additive and does not weaken existing contracts.
## Current v1 coverage
Green reference machines:
- Popover
- Tooltip
- Menu
- Select
- Combobox
- Cursor coordination
- Versioned async query channel
Amber:
- Dialog adapter, with documented restrictions.
Unknown:
- Native select popup open-state.
- Real-device IME behavior.
## Read next
- `SPEC.md`: v1 frozen scope and project claims.
- `KERNEL.md`: the state ownership model.
- `CONFORMANCE.md`: harness and CI behavior.
- `BUILDING_COMPONENTS.md`: how to add the next contract-backed component.
- `PROJECT_MAP.md`: where the detailed evidence lives in this repository.