| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
Some checks failed
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Has been cancelled
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Has been cancelled
standards workspaces build + test / jouleclaw-rs (pull_request) Has been cancelled
standards workspaces build + test / aac-rs (pull_request) Has been cancelled
standards workspaces build + test / arl-rs (pull_request) Has been cancelled
standards workspaces build + test / eoc-rs (pull_request) Has been cancelled
standards workspaces build + test / jcp-rs (pull_request) Has been cancelled
standards workspaces build + test / joule-code-rs (pull_request) Has been cancelled
standards workspaces build + test / joule-ui-rs (pull_request) Has been cancelled
standards workspaces build + test / joulecontract-rs (pull_request) Has been cancelled
standards workspaces build + test / jouledesk-rs (pull_request) Has been cancelled
standards workspaces build + test / joulehook-rs (pull_request) Has been cancelled
standards workspaces build + test / map-rs (pull_request) Has been cancelled
standards workspaces build + test / mesh-rs (pull_request) Has been cancelled
standards workspaces build + test / openpay (pull_request) Has been cancelled
standards workspaces build + test / proof-rs (pull_request) Has been cancelled
standards workspaces build + test / sandbox-rs (pull_request) Has been cancelled
standards workspaces build + test / sense-rs (pull_request) Has been cancelled
standards workspaces build + test / smart-byte-rs (pull_request) Has been cancelled
standards workspaces build + test / macOS energy backend (jcp-energy) (pull_request) Has been cancelled
joule-ui's energy record carries one figure per interaction and has no coverage field. Both `SpanMeter` adapters (joule-ui-serve's, and the examples' `support/span_meter.rs`) graded a span by its records alone, so a span from an NVML-only probe set (a Linux host whose only live counter is the GPU's: a `Gpu` record, `HwShunt`, and no CPU record at all) was sealed `HwShunt` for the whole interaction: the GPU's energy, passed off as a reading of a handler that ran on the CPU. `Span::stop`'s held-CPU rule does not reach it, because there is no CPU counter to have held. The rule now: a span with no hardware reading of the CPU (`Measurement::reads_cpu`) is sealed as the meter's `Estimator` figure for that span's wall time (`PowerMeter::estimate`, wall x the meter's TDP, never 0 for work that ran). A span that reads the CPU passes through unchanged, with any GPU record beside it (RAPL and NVML: the sum, `HwShunt`). A span that was already the estimator's record is replaced by the same figure. The rule is one function, `interaction_figure`, applied where each adapter closes a span. The examples' adapter is now generic over the same `Spans` seam joule-ui-serve's has (defaulting to `PowerMeter`), so it can be driven over scripted spans, and a new integration test includes it by `#[path]`. SPEC §5 gains the sentence the shared vendor-counter MUST implies for this format: with no coverage field, a figure from a counter that does not read the domain the work ran on (a GPU counter alone, for a handler on the CPU) MUST NOT be graded `HwShunt` for the interaction. joule-ui-serve's Cargo comment no longer lists NVML alone as a source of a `HwShunt` receipt. Tests: `a_gpu_only_span_is_sealed_as_an_estimate_never_as_a_reading`, in joule-ui-serve (through the real `Host`) and in joule-ui-interaction's new `tests/span_meter.rs` (through the real `run_interaction` and the examples' adapter): a scripted 8 ms GPU-only span (9,000 uJ `HwShunt`) is sealed as 160,000 uJ `Estimator` with no measured figure; a CPU+GPU span is sealed `HwShunt` at their sum (10,500 uJ); a CPU-only span as itself; the chain verifies. joule-ui-serve, -interaction, -conformance, -mcp, -core, -a2ui and -render pass (72 tests); clippy adds nothing (one existing joule-ui-core lint); `real_meter` and `jcp_metered` run as before on this M5 Max (every interaction `Estimator`, the 15 J grant spent and the sixth denied). `gen_vectors` regenerated into a scratch directory is byte-identical to `conformance/v1/` (`diff -r` clean, MANIFEST included): the conformance pack's `StepMeter` is not a `SpanMeter`, and nothing in the signed core changed. Cargo.lock gains only the new sandbox-schema dev-dependency edge. Falsified, each restored from git and re-run green: either adapter sealing the span as it stands (`interaction_figure` returning it unchanged) fails its `a_gpu_only_span_is_sealed_as_an_estimate_never_as_a_reading`. Co-Authored-By: Claude Opus 5.5 <[email protected]> |
||
| .. | ||
| conformance | ||
| joule-ui-rs | ||
| LICENSE | ||
| README.md | ||
| SPEC.md | ||
joule-ui
A typed declarative protocol for agent-emitted user interfaces.
joule-ui is the sibling to 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 anAction, the invocation is an accountable event. Every fire names the principal that made it — adid:key, the same whether a person or an agent (the protocol draws no distinction; both are traced).run_interactionauthorizes 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 anInteractionLogseals 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 (SandboxPowerMeter) and the capability (JCP grant) are recognized edges; the receipt, the chain, and the lifecycle are joule-ui's. SeeSPEC.md§5–§7 andexamples/metered_surface.rsfor the full loop.
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).
Reference implementation in joule-ui-rs/: Apache-2.0.