Deep-dive on Mira Murati's Interaction Models paper confirmed the
2026 streaming primitive set (276B MoE / 12B active TML-Interaction-
Small, continuous interleaving of 200ms input + 200ms output,
multi-stream parallel audio/video/text). Cross-reference: the
generative-UI side converged on the same window — MCP Apps (Jan 26
2026), AG-UI, Google A2UI, Open-JSON-UI all settled on three rungs
(Static / Declarative / Generative) with iframe sandbox for the
generative rung, matching Ruben Casses's Postman keynote ladder.
The two axes — interaction tempo (Mira) and UI-generation rung
(Ruben) — are orthogonal. Wave-47 typed up the first axis. Wave-48
types the second + composes them.
Four deliverables:
1. jouleclaw-ui-output (new crate) — pure types.
- UiOutput { Static { component_id, props } | Declarative
{ schema_id, descriptor } | Generative { language, source,
capabilities } }. Each rung carries the engineering invariant
its trust level requires.
- SandboxCapability + CapabilityPolicy { Empty | Allow(set) }
with check_all() returning first denied — the JouleClaw
doctrine demands default-empty allow-lists.
- ComponentRegistry for Static-rung validation (registered
allow-list, idempotent on id).
- DEFAULT_GENERATIVE_HTML_BYTES = 32 KiB, _REACT_BYTES = 64 KiB
+ default_generative_budget() lookup.
- UI_OUTPUT_MIME = "application/x.jouleclaw-ui-output+json" —
stable MIME so a jouleclaw-stream::Tick can carry a UiOutput
in its bytes WITHOUT extending the tick schema (and breaking
wave-47's stream-window/1 conformance vectors).
- Canonical JSON (sorted keys) + content_hash + payload_bytes
for receipt content-addressing and rolling-conservation
accounting.
- 11 tests.
2. jouleclaw-ui-oracle (new crate) — per-rung Oracle backends.
- StaticUiOracle — component_id ∈ ComponentRegistry.
- DeclarativeUiOracle — schema_id matches + descriptor.kind ∈
allowed_kinds. (Schema validation of the full descriptor is
the renderer's job; this gate refuses unrecognised kinds
before the renderer is reached.)
- GenerativeUiOracle — CapabilityPolicy.check_all + per-
language byte budget + caller-configurable deny-substring
list (default empty; callers seed `"eval("`,
`"document.write("`, etc. per their threat model).
- All three implement the per-request jouleclaw-oracle::Oracle
trait so they compose via CompositeOracle alongside the
FuzzOracle and the future Lean Oracle, AND lift into the
streaming cascade via jouleclaw-stream-oracle::PerTickAdapter.
- 13 tests covering each rung's pass/fail paths + wrong-rung
rejection + composition.
3. jouleclaw-jcr1 — ui-output/1 schema.
- UiOutputReceipt { jc_receipt, id, closed_at, steward, rung,
content_hash, byte_length, granted_capabilities, verdict,
stream_id, window_id }. Optional stream_id + window_id
correlate the UI receipt with a stream-window/1 receipt for
full-session reconstruction.
- UiRungWire { static | declarative | generative };
UiVerdictWire { Pass { oracle_id } | Fail { subkind, message } }.
- 2 conformance vectors (Static Pass with stream correlation;
Generative Fail standalone) with embedded receipt_hash the
Rust verifier asserts byte-equal at test time.
- 3 ui_output tests on top of the existing jcr1 surface.
4. Demo binary — examples/wave48_ui_streaming.rs in
jouleclaw-stack. Four UI outputs ship through the gates: one
per rung passes (Static / Declarative / Generative-with-
clipboard), plus one Generative-with-net.fetch that's correctly
denied by the capability policy. Each render mints a ui-output/1
receipt correlated to a streaming-window id.
Composition summary across waves 46-48:
per-request: Candidate → CompositeOracle (Fuzz | UiRung*)
→ Verdict → JCR-1 verified-code/1
OR JCR-1 ui-output/1
streaming-per-win: StreamWindow → CompositeStreamOracle (BargeIn
| PerTickAdapter<UiRungOracle>)
→ WindowVerdict → JCR-1 stream-window/1
+ RollingConservationLedger
+ per-output JCR-1 ui-output/1
(correlated by stream_id+window_id)
The triangle (conservation + cone + oracle + elicitor) types every
slot up; wave-46 / 47 / 48 fill backend slots without rewriting the
triangle itself. The MCP-Apps / AG-UI / A2UI ecosystem can target
jouleclaw-ui-output as the substrate-grade common type for what
an agent emits to the front-end, with receipts proving which rung
shipped, which capabilities were granted, and which gate signed
off.
Workspace check clean.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
50 lines
2.2 KiB
JSON
50 lines
2.2 KiB
JSON
{
|
|
"spec": "JCR-1 ui-output v1",
|
|
"schema_id": "ui-output/1",
|
|
"canonical_encoding": "JSON with sorted object keys (recursively); UTF-8; no whitespace; no float coercion. receipt_hash is BLAKE3 hex of the canonical bytes.",
|
|
"note": "Two vectors covering the two extremes of the UI-output ladder under JCR-1: a Static-rung Pass that ships inside a streaming window (correlated by stream_id + window_id), and a Generative-rung Fail (capability denied) that ships outside a streaming session (empty stream correlation). Implementers in any language MUST round-trip every receipt under canonical encoding and assert byte-equal receipt_hash against the embedded value.",
|
|
"vector_count": 2,
|
|
"vectors": [
|
|
{
|
|
"n": 1,
|
|
"description": "Static Pass — registered component rendered inside stream-demo window 3.",
|
|
"receipt_hash": "07a1da03f2c1e4ec881c9b00a5e1efca36c562cc665bc792ff8a38d68c561359",
|
|
"receipt": {
|
|
"jc_receipt": "ui-output/1",
|
|
"id": "ui-001",
|
|
"closed_at": "2026-06-05T00:00:00Z",
|
|
"steward": "did:keri:EJ9testStewardAID000000000000000",
|
|
"rung": "static",
|
|
"content_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
"byte_length": 128,
|
|
"granted_capabilities": [],
|
|
"verdict": {
|
|
"kind": "pass",
|
|
"oracle_id": "static-ui-gate"
|
|
},
|
|
"stream_id": "stream-demo",
|
|
"window_id": 3
|
|
}
|
|
},
|
|
{
|
|
"n": 2,
|
|
"description": "Generative Fail — requested capability `net.fetch` not in host policy; standalone (no stream correlation).",
|
|
"receipt_hash": "2e9b216eecee0253a1834eeaebdaea5354fe638db901ff6337cf0bb3e35b3aa5",
|
|
"receipt": {
|
|
"jc_receipt": "ui-output/1",
|
|
"id": "ui-002",
|
|
"closed_at": "2026-06-05T00:00:01Z",
|
|
"steward": "did:keri:EJ9testStewardAID000000000000000",
|
|
"rung": "generative",
|
|
"content_hash": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
"byte_length": 4096,
|
|
"granted_capabilities": [],
|
|
"verdict": {
|
|
"kind": "fail",
|
|
"subkind": "disallowed-capability",
|
|
"message": "requested `net.fetch` not in capability policy"
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|