Every Ed25519 signature WAI defines is verified strictly, in order: a canonical key, a key not of small order, S < L, R not of small order, and the cofactorless equation with R compared byte for byte. OER/2's chain signature keeps the rule oer-receipts §5 settles. jwp-receipts §3.1 step 3, http-delivery's playlist receipt and packaging's receipt cite it instead of restating it; the worlds, avatars, feeds, spatial-audio, haptics, volumetric, score, film, provenance, ternary and quantum receipt texts cite it where they name their signature. CHANGELOG records the sweep. Co-Authored-By: Claude Opus 5.5 <[email protected]>
581 lines
26 KiB
Markdown
581 lines
26 KiB
Markdown
# WAI Extension: Interactive Worlds
|
||
|
||
> Status: Draft. Sub-spec of WAI v1.0/v1.1. 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") |
|
||
+-----------------------------------------------------------------+
|
||
```
|
||
|
||
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 real energy via the sink-metering
|
||
path, which needs **no elevated privileges**: on arm64 macOS the SoC
|
||
energy counters are readable through IOReport (the source `macmon` /
|
||
`asitop` use — no sudo), on x86 via RAPL, and per-process retired
|
||
cycles/instructions via `proc_pid_rusage` everywhere. The reference
|
||
`wai_world_meter` brackets a replay burst against an idle baseline and
|
||
reports the marginal measured CPU energy. 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 what it measures `OnChipCounter`, with its declared uncertainty;
|
||
by §2's definitions, a browser's wall-clock figure against a declared
|
||
coefficient is `ModelBased`.
|
||
|
||
---
|
||
|
||
## 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.
|