Some checks failed
joule-moq-room room service / joule-moq-room · does this change touch it (push) Successful in 28s
joule-moq-remote host agent / joule-moq-remote · does this change touch it (push) Successful in 32s
WAI operation catalogue / does this change touch it (push) Successful in 25s
wai-fleet service / does this change touch it (push) Successful in 27s
wai-app and Create / wai-app · does this change touch it (push) Successful in 29s
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Successful in 30s
joule-code conformance gate / joule-moq-relay, its own energy on a counter (macos) (push) Successful in 28s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (push) Successful in 1m51s
wai-fleet service / service · macos (push) Successful in 1m9s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Successful in 5m47s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Successful in 12m44s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 8s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Successful in 48s
wai-fleet service / service · linux (push) Successful in 2m39s
joule-code conformance gate / Build + round-trip C1-C4 vectors (push) Successful in 6m0s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 52s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 3m31s
WAI operation catalogue / the catalogue and every door · linux (push) Successful in 9m13s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Has been cancelled
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (push) Has been cancelled
joule-moq-room room service / joule-moq-room, every target (push) Has been cancelled
joule-code conformance gate / joule-moq-relay, every target (push) Has been cancelled
joule-code conformance gate / joule-moq-ingest, every target (push) Has been cancelled
SPEC states the rule after its status line and describes the manifest's wai member as the envelope's wire version; its title, the four extension headers, the README and two code comments no longer call the standard itself v1.0. Text that names an envelope by its wire version is unchanged. Co-Authored-By: Claude Opus 5.5 <[email protected]>
583 lines
26 KiB
Markdown
583 lines
26 KiB
Markdown
# WAI Extension: Interactive Worlds
|
||
|
||
> Status: Draft. Sub-spec of WAI, for both envelopes (`WAI1` and `WAI2`). Reference implementation:
|
||
> the `world` feature of `wai-rs` (container, ops, fixed-point, replay
|
||
> + live engines, `kinematic0` + `ballistic0` sim registrants).
|
||
> Conformance corpus: `world-conformance/` at the WAI repo root.
|
||
|
||
Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY**, **REQUIRED**,
|
||
**OPTIONAL** are RFC 2119 / RFC 8174.
|
||
|
||
---
|
||
|
||
## 1. Scope and model
|
||
|
||
This extension registers a cargo class for **interactive worlds**:
|
||
media that consumes input. A world is a deterministic, content-
|
||
addressed artifact; everything that ever happens in it is a signed
|
||
**operation**; and any sequence of operations replays to a bit-exact
|
||
state on every conformant sink.
|
||
|
||
The single-player input log and the multiplayer operation log are the
|
||
**same object**. An input event is an operation emitted by one actor.
|
||
One actor's log is a single-player session; N interleaved actor logs
|
||
are a shared world; a sealed log is a replay. This extension defines
|
||
the operation once and derives all three forms from it.
|
||
|
||
In scope:
|
||
|
||
- Three registered capabilities: `wai.world.scene`,
|
||
`wai.world.replay`, `wai.world.live` (§3).
|
||
- The world-container payload format the three capabilities share (§4).
|
||
- The signed operation (`CausalOp`) wire format and its ordering,
|
||
causality, and convergence rules (§5).
|
||
- The simulation contract: tick semantics, deterministic numeric
|
||
profiles, seeded randomness, conflict resolution (§6).
|
||
- State hashing, checkpoints, and the **replay-equivalence**
|
||
conformance criterion (§7).
|
||
- Session receipts, as a profile of the JWP receipt chain (§8).
|
||
- The fallback floor: how a world degrades to passive media on sinks
|
||
below the capability floor (§9).
|
||
|
||
Out of scope:
|
||
|
||
- **A transport.** Live sessions need ops moved between actors; this
|
||
extension defines the op bytes and their semantics, not the wire
|
||
that carries them (WebRTC, WebSocket, MoQT, sneakernet — all
|
||
conformant if the bytes arrive intact).
|
||
- **An engine.** Rendering, spatial indexing, rollback strategy,
|
||
level-of-detail, asset pipelines, and solver implementations are
|
||
sink-side choices. The extension constrains *state*, never *how a
|
||
sink derives it efficiently*.
|
||
- **Sessions, identity, matchmaking.** Actor keys are Ed25519 public
|
||
keys; how actors find each other and authorize entry is the
|
||
deployer's business.
|
||
- **Visual conformance.** Conformance is state-level (§7). Pixel
|
||
output is the renderer's business; deployments that need pinned
|
||
visual output compose this extension with the existing
|
||
hash-pinned-prior mechanisms in the core spec.
|
||
|
||
Relationship to WAI SPEC.md:
|
||
|
||
- §2 (container) — unchanged. World payloads ride the `WAI1`/`WAI2`
|
||
envelopes as opaque capability-owned bytes, per SPEC.md §5: "each
|
||
capability owns its payload format."
|
||
- §3 (manifest) — unchanged. `media` takes the new informational value
|
||
`"world"`; the capability string remains authoritative. The
|
||
simulation contract lives in the payload (§4), not the manifest.
|
||
- §4 (dispatch) — unchanged. A sink without `wai.world.*` falls back
|
||
per the declared `fallback`, or reports the file inert.
|
||
- §6.1 (multi-rendition) — the `WAI2` envelope is the natural carrier
|
||
for a world plus its passive-media floor rendition (§9).
|
||
- §7 (versioning) — these capabilities are added by registration in a
|
||
MINOR revision; the envelope is unchanged.
|
||
- `extensions/jwp-receipts.md` — session receipts (§8) are a profile
|
||
of the JWP group receipt; nothing there is modified.
|
||
|
||
Precedent inside the core spec: `wai.neural.int_synth` already
|
||
registers a capability whose defining property is **bit-identical
|
||
output on every machine**. This extension generalizes that property
|
||
from decode to simulation.
|
||
|
||
---
|
||
|
||
## 2. Terminology
|
||
|
||
- **World** — a content-addressed graph of nodes plus a simulation
|
||
contract. Identified by the BLAKE3-256 of its canonical
|
||
serialization (§7.1).
|
||
- **Node** — the unit of identity in a world. 128-bit id (§5.4).
|
||
- **Actor** — the holder of an Ed25519 keypair who emits operations.
|
||
- **Operation (op)** — a signed structural mutation targeting a node,
|
||
bound to an effect tick (§5).
|
||
- **Tick** — the discrete simulation step. All state is defined
|
||
per-tick; nothing is defined between ticks.
|
||
- **Checkpoint** — a tick at which the state hash is computed and
|
||
(in receipts and sealed replays) recorded.
|
||
- **Sealed log** — an op log with a terminal marker and final state
|
||
hash; the replay form.
|
||
|
||
---
|
||
|
||
## 3. Registered capabilities
|
||
|
||
| capability | payload | role |
|
||
|---|---|---|
|
||
| `wai.world.scene` | world container, op log empty | a world at tick 0 — a shareable, explorable artifact |
|
||
| `wai.world.replay` | world container, op log sealed | a finished session — re-derives every state from kilobytes |
|
||
| `wai.world.live` | world container, op log open | a joinable world — ops continue to arrive by out-of-scope transport |
|
||
|
||
All three share the container format (§4) and differ only in the op
|
||
log's seal state. A sink that advertises any `wai.world.*` capability
|
||
MUST implement all of §§4–7; `wai.world.live` additionally requires
|
||
the live-ingestion rules in §5.6.
|
||
|
||
`intent` for all three is `replicate`: the sink reconstructs exactly
|
||
the state the ops define. Generative intents over worlds are not
|
||
defined in this revision.
|
||
|
||
---
|
||
|
||
## 4. World container (payload format)
|
||
|
||
The payload bytes for every `wai.world.*` capability:
|
||
|
||
```
|
||
+--------+----------------+------------------+----------...----+
|
||
| "WWLD" | u16 n_sections | section table | sections |
|
||
| 4 B | little-endian | n × (u8 kind, | packed, |
|
||
| | | u32 off,len) | 8-byte aligned |
|
||
+--------+----------------+------------------+-----------------+
|
||
```
|
||
|
||
Section offsets are relative to the start of the sections block;
|
||
each section is zero-padded to an 8-byte boundary; `len` is the
|
||
unpadded body length.
|
||
|
||
Section kinds:
|
||
|
||
| kind | section | required |
|
||
|---|---|---|
|
||
| `0x01` | simulation contract (§6), canonical JSON | REQUIRED |
|
||
| `0x02` | world graph: canonical state bytes at tick 0 (§7.1 format) | REQUIRED |
|
||
| `0x03` | asset table (§4.1) | OPTIONAL |
|
||
| `0x04` | op log (§5), concatenated `CausalOp` records | REQUIRED (may be empty) |
|
||
| `0x05` | seal: final tick (u64) + final state hash (32 B) + publisher Ed25519 signature (64 B) | REQUIRED for `wai.world.replay`, MUST be absent otherwise |
|
||
|
||
Unknown section kinds MUST be ignored (forward compatibility within
|
||
this extension's MINOR revisions).
|
||
|
||
The seal signature is computed over:
|
||
|
||
```
|
||
seal_preimage = "wai:world-seal\x01"
|
||
|| BLAKE3(contract section bytes)
|
||
|| BLAKE3(graph section bytes)
|
||
|| BLAKE3(op-log section bytes)
|
||
|| final_tick (u64 LE) || final_hash (32 B)
|
||
```
|
||
|
||
Hashing the three sections — rather than raw container bytes — makes
|
||
the commitment independent of section-table order and padding: the
|
||
seal binds content, not framing. The seal's signature, every op's
|
||
(§5.1) and the session receipt's (§8) are verified as SPEC §7
|
||
*Signatures* requires.
|
||
|
||
### 4.1. Asset table — worlds are made of WAI objects
|
||
|
||
Each asset entry is:
|
||
|
||
```
|
||
asset_hash (32 B BLAKE3-256) | u8 role | u32 name_len | name (UTF-8)
|
||
```
|
||
|
||
`asset_hash` is the content hash of a **complete WAI envelope** —
|
||
computed exactly as in jwp-receipts §2.1, same domain separation
|
||
discipline, domain `"wai:asset\x01"`. An asset is itself a WAI object
|
||
(a JPEG-XL texture, a FLAC ambience, a zstd-compressed mesh) and is
|
||
fetched, verified, and decoded by ordinary WAI capability dispatch.
|
||
The world container never embeds asset bytes; it names them.
|
||
|
||
Consequences, all deliberate:
|
||
|
||
- Assets dedupe globally across worlds (content addressing).
|
||
- Asset delivery inherits the JWP receipt chain unchanged.
|
||
- New asset media (mesh, material, armature codecs) enter by ordinary
|
||
capability registration without touching this extension.
|
||
|
||
Assets MUST NOT influence simulation state. They bind to nodes for
|
||
*presentation*; a sink missing an asset MUST still simulate
|
||
identically (and SHOULD present a declared placeholder). This is what
|
||
keeps conformance state-level while assets stream in.
|
||
|
||
For geometry, materials, textures, and skeletal armatures the
|
||
registered asset capability is **`wai.mesh.glb`**: the payload is a
|
||
canonical glTF 2.0 Binary (`.glb`, Khronos, royalty-free) — the same
|
||
zeroth-condition approach as the rest of the core registry: dispatch to
|
||
a published standard. Decoders are sink- or
|
||
library-supplied (every engine and DCC tool ships one). Further asset
|
||
media register in the core capability table as usual.
|
||
|
||
---
|
||
|
||
## 5. Operations
|
||
|
||
### 5.1. CausalOp wire format
|
||
|
||
```
|
||
+-----------------------------------------------------------------+
|
||
| actor_id (32 B Ed25519 pubkey) | lamport (u64) | effect_tick(u64)|
|
||
+-----------------------------------------------------------------+
|
||
| n_deps (u16) | deps: n × (32 B actor_id + u64 lamport) |
|
||
+-----------------------------------------------------------------+
|
||
| target_node (16 B) | mutation_kind (u16) | mut_len (u32) |
|
||
+-----------------------------------------------------------------+
|
||
| mutation bytes (mut_len, owned by the sim contract §6.4) |
|
||
+-----------------------------------------------------------------+
|
||
| signature (64 B Ed25519, over all preceding bytes, |
|
||
| domain "wai:world-op\x01") |
|
||
+-----------------------------------------------------------------+
|
||
```
|
||
|
||
The integers are little-endian, and the signature is Ed25519 over the domain followed by
|
||
every byte before the signature. An op's identity is `op_id = (actor_id, lamport)`. An actor MUST
|
||
increment `lamport` per op emitted and MUST set it greater than every
|
||
`lamport` it has observed (standard Lamport discipline).
|
||
|
||
### 5.2. Canonical order
|
||
|
||
The canonical total order over any op set is:
|
||
|
||
```
|
||
(effect_tick, lamport, actor_id) — ascending, bytewise on actor_id
|
||
```
|
||
|
||
This order is a pure function of op content. Two sinks holding the
|
||
same op set hold the same canonical order, always.
|
||
|
||
### 5.3. Causality
|
||
|
||
`deps` lists op_ids this op observed. Deps gate **admission**, not
|
||
ordering: a sink MUST NOT admit an op whose deps are not all already
|
||
admitted — it buffers the op until they are. Once admitted, ops apply
|
||
strictly in canonical order (§5.2); deps express observation, the
|
||
canonical key is the ordering authority. In the sealed-replay form
|
||
the log MUST be listed in strictly ascending canonical order (which
|
||
also forbids duplicate op ids) and every dep MUST name an op present
|
||
in the log; a sink MUST reject a sealed log that is dep-incomplete or
|
||
mis-ordered.
|
||
|
||
Redelivery of a byte-identical, already-admitted op is an idempotent
|
||
no-op (transports redeliver; redelivery must not be an error). A
|
||
second op with the same `(actor, lamport)` but different content is
|
||
**equivocation** and MUST be rejected.
|
||
|
||
### 5.4. Node identity and minting
|
||
|
||
Node ids are 128 bits. A node created by op `o` has
|
||
`node_id = BLAKE3(domain || actor_id || lamport)[0..16]`, domain
|
||
`"wai:world-node\x01"`. Minting is therefore deterministic,
|
||
collision-resistant, and requires **no coordination between actors**
|
||
— two actors creating nodes concurrently can never collide.
|
||
Tick-0 nodes in the world graph carry explicit ids assigned by the
|
||
authoring encoder under the same scheme with the publisher as actor.
|
||
|
||
### 5.5. Convergence requirement (normative core)
|
||
|
||
> Any conformant sink holding the same world container and the same
|
||
> op set MUST compute the identical state hash at every tick,
|
||
> regardless of arrival order.
|
||
|
||
This holds by construction: canonical order (§5.2) is content-derived,
|
||
conflict resolution (§6.5) is deterministic, and the numeric profile
|
||
(§6.2) is bit-exact. Sinks are free to apply ops optimistically and
|
||
re-derive when late ops arrive (speculation, rollback, forward-replay)
|
||
— *how* is out of scope; converging is not.
|
||
|
||
### 5.6. Live ingestion
|
||
|
||
A `wai.world.live` sink MUST: verify each arriving op's signature,
|
||
buffer dep-incomplete ops, and insert applicable ops into canonical
|
||
order — re-deriving state from the most recent checkpoint at or
|
||
before the op's `effect_tick` when an op arrives for a past tick.
|
||
A sink MUST reject ops whose `effect_tick` precedes the latest
|
||
**sealed** checkpoint it has receipted (§8): receipted history is
|
||
immutable.
|
||
|
||
---
|
||
|
||
## 6. Simulation contract
|
||
|
||
Section `0x01`, canonical JSON (no insignificant whitespace, sorted
|
||
keys — this section is hashed):
|
||
|
||
```json
|
||
{
|
||
"tick_rate": 60,
|
||
"numeric": "wai.det.fixed64",
|
||
"rng": { "alg": "xoshiro256++", "seed": "<hex 32 B>" },
|
||
"sim": "<sim-capability-string>",
|
||
"resolve": { "default": "wai.world.resolve.lww" },
|
||
"checkpoint_every": 600
|
||
}
|
||
```
|
||
|
||
Canonical emission: keys sorted (`checkpoint_every, numeric, resolve,
|
||
rng, sim, tick_rate`; nested objects likewise), lowercase hex, no
|
||
insignificant whitespace. Readers MUST NOT depend on key order for
|
||
semantics (same rule as the core manifest); the canonical bytes are
|
||
what the container carries and the seal commits to. The rng seed is
|
||
the xoshiro256++ state as four little-endian u64 words; the all-zero
|
||
seed (the family's one degenerate point) maps to the first four
|
||
outputs of `splitmix64(0)`, normatively.
|
||
|
||
### 6.1. Ticks
|
||
|
||
State is a pure function `state(t+1) = step(state(t), ops_at(t+1))`.
|
||
`tick_rate` is the *semantic* rate binding ticks to wall-clock for
|
||
presentation; simulation correctness never depends on wall-clock.
|
||
|
||
### 6.2. Numeric profiles
|
||
|
||
Registered in this revision:
|
||
|
||
| profile | definition | status |
|
||
|---|---|---|
|
||
| `wai.det.fixed64` | Q32.32 signed fixed-point; integer add/sub/mul/div with defined rounding (toward negative infinity) and saturation; integer-only transcendental tables published in the conformance corpus | REQUIRED for any `wai.world.*` sink |
|
||
| `wai.det.f32strict` | IEEE 754 binary32, round-to-nearest-even, FMA forbidden, fast-math forbidden, evaluation order as written in the sim capability's normative pseudocode | OPTIONAL |
|
||
|
||
`wai.det.fixed64` is the floor for the same reason PNG/FLAC/zstd are
|
||
the core floor: it is satisfiable on **every** machine, including ones
|
||
whose float units disagree. Bit-exactness is the requirement of
|
||
existence here; profiles that cannot guarantee it cannot be registered.
|
||
|
||
### 6.3. Randomness
|
||
|
||
All randomness flows from the seeded generator named in `rng`,
|
||
advanced only by simulation steps in canonical order. Sink-local
|
||
randomness MUST NOT touch state.
|
||
|
||
### 6.4. The sim capability
|
||
|
||
`sim` names the capability that owns `mutation_kind` semantics and the
|
||
`step` function — the actual physics. This extension deliberately does
|
||
not standardize a physics engine; it standardizes the **contract any
|
||
physics must honor**: deterministic under §6.2, pure per §6.1,
|
||
convergent per §5.5. Sim capabilities register like any WAI
|
||
capability, each with normative pseudocode and conformance vectors.
|
||
|
||
The reference registrant is `wai.world.sim.kinematic0` (point-mass
|
||
kinematics + one scalar field per node, all `wai.det.fixed64`): five
|
||
mutation kinds (CREATE/DELETE/SET_SCALAR/SET_POS as `lww`, IMPULSE as
|
||
`sum`; ops targeting absent nodes are deterministic no-ops; CREATE
|
||
targets MUST equal the §5.4 minted id), normative Rust in
|
||
`wai-rs/src/world/sim.rs`, exercised by the `world-conformance/`
|
||
corpus. Deliberately small — rich enough to exercise every normative
|
||
mechanism, simple enough that an independent implementation is an
|
||
afternoon. A second registrant, `wai.world.sim.ballistic0` (gravity +
|
||
ground-plane bounce with per-node radius and restitution, registration
|
||
constant `G = −10`), exists to prove the registration story in
|
||
practice: it entered the registry, the dispatch, and the conformance
|
||
corpus without one line of this extension changing.
|
||
|
||
The solver-backed registrant is `wai.world.sim.truss0`: structural
|
||
simulation — point-mass particles joined by distance members, solved
|
||
per tick by **position-based dynamics** (a fixed-count Gauss–Seidel
|
||
constraint projection, `ITERATIONS = 8`, canonical member order,
|
||
`G = −10`, ground plane at `y = 0`, velocity damping `63/64` per tick
|
||
— exactly `1 − 2⁻⁶` in Q32.32, so structures reach static equilibrium
|
||
— all `wai.det.fixed64` including the floor-rounded square root). Members carry a yield strain measured
|
||
on predicted positions before the solve; an overloaded member
|
||
**fractures** as an emergent solver consequence — including under the
|
||
load two actors apply by pulling the same member in opposite
|
||
directions (the `sum` rule feeding the solver). Stress is computed,
|
||
not animated, and failure is bit-exact on every machine. Richer sims
|
||
(rigid-body contact, fields, FEM-class) register the same way.
|
||
|
||
Sinks dispatch on the contract's `sim` string. A sink that lacks the
|
||
named sim capability MUST report the world inert — the same
|
||
missing-capability semantics as SPEC.md §4 step 6 — so the caller can
|
||
fall back per the envelope's declared `fallback`.
|
||
|
||
### 6.5. Conflict resolution
|
||
|
||
Two ops **conflict** when they share an `effect_tick` and target the
|
||
same node with mutations the sim capability declares non-commuting.
|
||
Resolution functions registered in this revision:
|
||
|
||
| function | rule |
|
||
|---|---|
|
||
| `wai.world.resolve.lww` | discrete/scalar conflicts: last writer in canonical order wins (canonical order is total, so "last" is deterministic) |
|
||
| `wai.world.resolve.sum` | continuous conflicts: concurrent mutations combine under a commutative monoid declared by the sim capability (e.g. force vectors sum; both inputs act, neither is discarded) |
|
||
|
||
A sim capability MAY map mutation kinds to either function but MUST
|
||
declare the mapping statically in its registration. Because `lww`
|
||
rides the canonical total order and `sum` is commutative-associative,
|
||
both are order-independent — convergence (§5.5) survives any arrival
|
||
order. Downstream physical consequences of combined mutations
|
||
(stress, fracture, overflow) are ordinary sim behavior, not
|
||
resolution.
|
||
|
||
---
|
||
|
||
## 7. State hashing and conformance
|
||
|
||
### 7.1. State hash
|
||
|
||
```
|
||
state_hash(t) = BLAKE3(domain || canonical_state_bytes(t))
|
||
domain = "wai:world-state\x01"
|
||
```
|
||
|
||
`canonical_state_bytes` is:
|
||
|
||
```
|
||
n_nodes (u32 LE) | n × ( node_id (16 B) | state_len (u32 LE) | state bytes )
|
||
```
|
||
|
||
with nodes in strictly ascending node-id order and each node's state
|
||
fields in the order the sim capability's registration declares. The
|
||
WWLD graph section (§4) is this same format at tick 0 — the authored
|
||
graph IS canonical state bytes. The asset table and presentation data
|
||
are excluded (§4.1).
|
||
|
||
### 7.2. Checkpoints
|
||
|
||
A sink MUST compute `state_hash` every `checkpoint_every` ticks and at
|
||
the final tick of a sealed log. Sealed replays carry the final hash in
|
||
the seal section; a mismatch on replay is a conformance failure of
|
||
either the log or the sink — the file MUST be reported failed, never
|
||
presented as approximately right.
|
||
|
||
### 7.3. Conformance criterion
|
||
|
||
The core spec's criterion is bit-exact decode-equivalence. This
|
||
extension's criterion is its temporal generalization:
|
||
|
||
> **Replay-equivalence.** Given the same world container and op set, a
|
||
> conforming sink MUST produce the identical `state_hash` at every
|
||
> checkpoint, on every machine, under every arrival order permitted by
|
||
> §5.
|
||
|
||
The conformance corpus lives at `world-conformance/` (alongside
|
||
`byte-exact-conformance/`): sealed world containers, live op streams
|
||
in adversarial arrival orders (dep-gaps, past-tick arrivals, seeded
|
||
shuffles, conflict batteries for both resolution functions), and
|
||
expected checkpoint hashes. A sink passes by hash equality; there is
|
||
no tolerance parameter. The reference implementation is the `world`
|
||
feature of `wai-rs` (pure Rust, no ML dep chain); the corpus is
|
||
generated and checked by `wai_world_vectors gen|verify`.
|
||
|
||
### 7.4. What replay-equivalence buys
|
||
|
||
Stated once, since it is the point of the extension:
|
||
|
||
- A **recording of play is kilobytes**: world hash + op log. Any sink
|
||
re-derives every frame. The largest interactive-video category on
|
||
the internet becomes instructions, not pixels.
|
||
- Two actors who opened the same hash and applied the same ops are
|
||
**provably in the same world** — not probably, provably, by hash.
|
||
- A dropped connection recovers by **fetching missing ops and
|
||
replaying forward** from the last checkpoint — state snapshots are a
|
||
sink-side cache, never the artifact.
|
||
|
||
---
|
||
|
||
## 8. Session receipts (JWP profile)
|
||
|
||
A world session is receipted as a JWP group (jwp-receipts.md,
|
||
unchanged):
|
||
|
||
- **Objects** in the group are op batches: each batch is the
|
||
concatenated `CausalOp` bytes for a checkpoint interval, content-
|
||
hashed per jwp-receipts §2.1 with domain `"wai:world-ops\x01"`.
|
||
- The group's canonical metadata carries, in addition to the JWP base
|
||
fields: the world hash, the checkpoint `state_hash` list for the
|
||
interval, and the publisher's measured joules for the interval
|
||
(source-side authoring/solver joules; sink-side joules append per
|
||
the JWP sink-metering path).
|
||
- One Ed25519 signature per group, Merkle-bound, relay-preserved
|
||
byte-for-byte — all inherited — verified as SPEC §7 *Signatures* requires.
|
||
- The signing domain is the profile's own, `wai:world-receipt\x01`
|
||
(`\x02` when the figure carries a label; energy-measurement.md
|
||
§2.8), not the base group receipt's `moq-jwp:group-sign\x01`.
|
||
|
||
A receipted checkpoint **seals history**: §5.6 forbids ops landing
|
||
before it. The receipt chain therefore gives a shared world an
|
||
append-only, signed, energy-accounted timeline — the same artifact
|
||
JWP gives a video stream, generalized to state.
|
||
|
||
**Chaining (`parent_receipt_hash`).** Each receipt carries the BLAKE3
|
||
of the previous receipt's canonical bytes (the JWP linkage), or `null`
|
||
for the first. Successive sealed sessions of one world (shared
|
||
`world_hash`, `group_id` incrementing by one) thus form one verifiable
|
||
append-only timeline; a verifier walks the chain checking each
|
||
signature and each parent link. The reference engine implements this
|
||
as `SessionReceipt::parent_receipt_hash` + `receipt_hash()` +
|
||
`verify_chain`.
|
||
|
||
**Energy, honestly.** A receipt carries two distinct quantities:
|
||
- `work_steps` — the **exact, deterministic** node-step count
|
||
(Σ over ticks of the node count integrated that tick). It is part of
|
||
the signed payload and reproducible on any sink, because it is a
|
||
function of the world and its ops alone.
|
||
- `joules_micro` — a **carried, measured** figure, never derived inside
|
||
the receipt. Native sinks supply energy via the sink-metering path,
|
||
which needs **no elevated privileges**: on arm64 macOS a processor
|
||
power a non-privileged reader reports, and the kernel's per-process
|
||
energy (`proc_pid_rusage`); on x86 RAPL. The reference
|
||
`wai_world_meter` brackets a replay burst against an idle baseline and
|
||
reports the marginal CPU energy of the reported power. A browser, lacking those
|
||
counters, supplies a wall-clock estimate against a **declared** power
|
||
coefficient and labels it an estimate; an unmetered sink signs the
|
||
unmetered marker `0`, which a reader presents as unmetered and never as a
|
||
figure of zero joules ([energy-measurement](energy-measurement.md) §2).
|
||
Joules are derived *from* measurement or a stated model, never
|
||
asserted in place of one. The figure's acquisition class rides beside
|
||
it as an optional signed label
|
||
([energy-measurement](energy-measurement.md) §2.8): absent, the receipt
|
||
keeps its legacy bytes and the figure is unlabelled; `wai_world_meter`
|
||
labels its figure `ModelBased`, because its samples are a power another
|
||
program reports, not a counter it reads and shows live
|
||
([energy-measurement](energy-measurement.md) §3.1); by §2's definitions,
|
||
a browser's wall-clock figure against a declared coefficient is
|
||
`ModelBased` too.
|
||
|
||
---
|
||
|
||
## 9. Fallback floor
|
||
|
||
`model_requirement.fallback` for `wai.world.*` SHOULD name a passive
|
||
capability, and encoders SHOULD ship the `WAI2` multi-rendition form:
|
||
|
||
| primary | floor rendition |
|
||
|---|---|
|
||
| `wai.world.replay` | `wai.video.av1` — a rendered recording of the same replay |
|
||
| `wai.world.scene` | `wai.image.jxl` or `wai.image.png` — a rendered view |
|
||
| `wai.world.live` | `wai.video.av1` — a spectator stream rendered by a capable participant (production of that stream is out of scope) |
|
||
|
||
Below-floor sinks get ordinary WAI media through ordinary dispatch —
|
||
nothing about the core fallback machinery changes. The interactive
|
||
form and its floor travel in **one envelope**: the capable sink plays
|
||
the world; the legacy sink plays the video; both opened the same file.
|
||
|
||
---
|
||
|
||
## 10. Versioning
|
||
|
||
This extension follows SPEC.md §7. The `WWLD` section-table format and
|
||
the `CausalOp` layout are frozen once registered; new section kinds,
|
||
numeric profiles, sim capabilities, and resolution functions are added
|
||
by registration in MINOR revisions; redefining any registered string
|
||
requires a MAJOR.
|
||
|
||
---
|
||
|
||
## Appendix A — Design rationale (non-normative)
|
||
|
||
**Why one op format for input and multiplayer.** Deferring multiplayer
|
||
would have shipped a v1 input log that a later revision must break.
|
||
The unification is also the cheaper design: single-player is one
|
||
actor's log, a replay is a sealed log, a shared world is N logs merged
|
||
by canonical order. Three products, one definition.
|
||
|
||
**Why fixed-point is the floor.** Replay-equivalence with no tolerance
|
||
parameter is only honest if the arithmetic is bit-exact on every
|
||
machine that exists. Strict-IEEE float is achievable but
|
||
toolchain-fragile; integer arithmetic is not. The floor must be
|
||
unconditional — the same reasoning that put PNG, not AVIF, in the core
|
||
floor.
|
||
|
||
**Why the standard has no physics.** The moment the extension
|
||
specifies a solver it stops being a container standard and starts
|
||
being an engine, which an engine would have to replace rather than
|
||
implement. The contract — deterministic, pure, convergent, receipted — is
|
||
the only part that must be common.
|
||
|
||
**Why state-level, not pixel-level, conformance.** Pixels are a
|
||
function of state plus presentation assets plus renderer. State is the
|
||
shared truth two actors must agree on; presentation is the part sinks
|
||
legitimately differ on (resolution, quality tier, accessibility
|
||
rendering). Deployments needing pinned pixels can compose the world's
|
||
state determinism with the core spec's pinned-prior machinery — the
|
||
two guarantees stack.
|