open-standards/joule-ui/README.md
Transaction Science 67d2efdafd joule-ui: the line language (SPEC §11), a streaming ingest into validated widget trees
joule-ui takes in OpenUI Lang 1.0 the way it takes in A2UI: a model writes a
surface as a program, one widget a line, and joule-ui-lang builds a widget tree
against the host's registry as the program streams. After every chunk a snapshot
holds the tree as far as it can be built, each widget checked by joule-ui's own
validate, and every error with its source line and reason code. State, queries,
operators, conditionals, built-in functions and sequences of actions are parsed
and refused not-declarative. A canonical writer turns a tree back into the
language; it parses back to the same tree.

- joule-ui-core: WidgetSchema::positional, the order a positional call passes
  props in (SPEC §3); absent from the wire form when undeclared.
- joule-ui-interaction: begin_interaction and Pending::finish, the lifecycle's
  three phases in two steps for a host whose handler is asynchronous (SPEC §6);
  run_interaction is built on them and seals what it sealed before.
- conformance/lang/v1: L1 statements, L2 ingest, L3 streaming, L4 the canonical
  form, L5 refusals, with an independent Python verifier written from §11.
- docs/create.md: the design of Create.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-26 18:09:10 -04:00

80 lines
4.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# joule-ui
A typed declarative protocol for **agent-emitted user interfaces**.
joule-ui is the sibling to [JouleClaw](../jouleclaw/) for the moment when an
agent's resolution has to become something a human can see and interact
with. The agent emits a **widget tree** — `(name, props, children)`,
recursively — against a **widget registry** the host has published. The
host renders. No React in the spec, no HTML over the wire, no agent
holding the renderer.
The shape the field has converged on for this is *declarative components
against a registry*: agents are far more reliable emitting structured
data than emitting code, and renderers are far easier to harden when they
own their own widgets. joule-ui pins that shape as a protocol; the
renderer is product surface, owned by the consumer.
## What ships in v1
- `joule-ui-rs/crates/joule-ui-core/` — the protocol types (`Widget`,
`PropValue`, `WidgetSchema`, `Registry`) and the recursive validator.
Pure Rust, no IO, JSON-serialisable.
- `joule-ui-rs/crates/joule-ui-interaction/` (**v0.2**) — the layer no
other UI protocol carries: when a human *or an agent* fires an
`Action`, the invocation is an **accountable event**. Every fire names
the principal that made it — a `did:key`, the same whether a person or
an agent (the protocol draws no distinction; both are traced).
`run_interaction` authorizes it against a capability *before it runs*
(a denied action's handler never executes), meters it in joules,
charges its cost (a reading or an estimate, and which), and an
`InteractionLog` seals it as the next
link of a **hash-chained** receipt. The actor is bound into the signed
core, so an interaction cannot be re-attributed without breaking the
chain. A joule-ui session becomes a tamper-evident ledger of *who fired
it, what it computed, and what it cost* — verifiable by anyone holding
the host's key. The metering (Sandbox `PowerMeter`) and
the capability (JCP grant) are **recognized edges**; the receipt, the
chain, and the lifecycle are joule-ui's. See [`SPEC.md`](SPEC.md) §5–§7
and `examples/metered_surface.rs` for the full loop.
- `joule-ui-rs/crates/joule-ui-lang/` (**v0.3**) — the line language: a
model writes a surface as a short program, one widget a line
(OpenUI Lang 1.0; see [`NOTICE`](NOTICE)), and joule-ui takes it in as it
streams. After every chunk a snapshot holds the tree as far as it can be
built, each widget validated against the registry, and every error with its
line and reason; a canonical writer turns a tree back into the language.
State, queries, expressions and sequences of actions are refused
`not-declarative`: joule-ui carries no behaviour on the wire. See
[`SPEC.md`](SPEC.md) §11, the pack in [`conformance/lang/`](conformance/lang/)
with its independent verifier, and [`docs/create.md`](docs/create.md), the
design of Create, the page that builds a WAI app as a model describes it.
The surface layer makes joule-ui a peer of the declarative-UI protocols
(A2UI, MCP Apps). The interaction layer makes it the only one you can
hand to an auditor: a UI whose every interaction is energy-accounted and
sealed, by construction.
What is **not** here:
- A renderer. The renderer is owned by the host (browser app, native
shell, terminal UI). joule-ui validates the surface and seals the
interaction; it does not paint pixels.
- A transport. The validated spec and the interaction stream ride MCP,
REST, SSE, gRPC, Web MoQ — any bytes-out protocol. Transport-agnostic.
- A "model emits React" path. The agent emits a declarative tree against
the registry; arbitrary code emission is out of scope.
## Why a sibling, not inside JouleClaw
The spec layer is protocol — small, stable, conformant. The renderer
registry is product surface — many of them, evolving fast. Splitting
them protects the *tools for use* line: JouleClaw stays the runtime
substrate; joule-ui is the UI protocol; consumers ship their own
widgets.
## Licence
Specification text and protocol document: CC-BY-4.0 (see [`LICENSE`](LICENSE)).
Reference implementation in `joule-ui-rs/`: Apache-2.0 (see [`joule-ui-rs/LICENSE`](joule-ui-rs/LICENSE)).