open-standards/wai/extensions/haptics.md
Transaction Science 161d147767
Some checks failed
wai-quantum WebGPU member / member · discrete GPU, Vulkan (jetson-hub) (pull_request) Successful in 9m59s
spec citations resolve / Code cites no section its SPEC lacks (pull_request) Successful in 4s
standards workspaces build + test / arl-rs (pull_request) Successful in 1m12s
standards workspaces build + test / eoc-rs (pull_request) Successful in 2m15s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m48s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 1m10s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 1m13s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m35s
standards workspaces build + test / sense-rs (pull_request) Successful in 1m14s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m35s
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Has been cancelled
no-vanity-metrics / WAI public text names no comparison (pull_request) Has been cancelled
no-vanity-metrics / WAI public text names no company or product (pull_request) Has been cancelled
no-vanity-metrics / No new orphan source files (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 / joule-code-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 / 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 / macOS energy backends (pull_request) Has been cancelled
tests in CI / every test file is run by a job or listed with its reason (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · macos (pull_request) Has been cancelled
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Has been cancelled
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Has been cancelled
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Has been cancelled
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · macos (pull_request) Has been cancelled
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Has been cancelled
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Has been cancelled
WAI energy receipts / energy receipts · macos (pull_request) Has been cancelled
WAI HTTP delivery / WAI HTTP · does this change touch it (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify mutations 0/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify mutations 1/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify mutations 2/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify patches 0/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify patches 1/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify patches 2/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · every patch and mutation in exactly one shard (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · verified both ways, and the differential (pull_request) Has been cancelled
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Has been cancelled
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Has been cancelled
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Has been cancelled
wai-pack / wai-pack · does this change touch it (pull_request) Has been cancelled
wai-pack / wai-pack · linux (pull_request) Has been cancelled
wai-pack / wai-pack · linux-arm64 (pull_request) Has been cancelled
wai-pack / wai-pack · macos (pull_request) Has been cancelled
wai-pack / wai-pack · independent verifier (Python) (pull_request) Has been cancelled
wai-pack / wai-pack · the floor through a media prober (pull_request) Has been cancelled
wai-pack / wai-pack · dependency licences (pull_request) Has been cancelled
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness · MSRV (pull_request) Has been cancelled
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · Metal (macos) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · browser WebGPU (macos) (pull_request) Has been cancelled
WAI section references / cited sections exist (pull_request) Has been cancelled
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Has been cancelled
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Has been cancelled
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Has been cancelled
workflow hygiene / every workflow supersedes its own queue (pull_request) Has been cancelled
lockfiles / every Cargo.lock is current (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux (pull_request) Has been cancelled
standards workspaces build + test / smart-byte-rs (pull_request) Has been cancelled
wai: reader limits for the extension formats' durations
A score, haptic signal, audio scene, splat4d clip or film declares its length in its
contract, and rendering allocates in proportion: a score's mixdown allocated `duration`
samples outright, a haptic channel `duration` × 2 bytes, a scene's stereo mix 20 bytes a
sample, and the splat4d and film receipts a hash a frame. Nothing bounded the
declaration, so a few bytes could ask a renderer for more memory than any machine has.
SECURITY.md recorded it as a gap when the fuzzing PR found the reader-side reservations.

Each extension's contract section (score, haptics, spatial-audio, volumetric,
linear-film and its reel) now has a reader limit shared with SPEC §2, through a new
always-on module `wai::limits`:
- score and haptics: `duration × channels` at most 2^27 samples;
- audio scene: `duration` at most 2^26, so the stereo floor is at most 2^27 samples;
- splat4d `n_frames`, film `frames`, a reel's shots together: at most 2^20 frames.
A reader may refuse past them (`duration_too_long`) when it reads the contract, before
anything is rendered; a conforming encoder writes none.

The film schedule's tick, `start_tick + f × ticks_per_frame`, wrapped in release builds
and panicked in debug ones. A reader must now refuse a schedule whose last tick passes
2^64 - 1 (a film's, and each shot's of a reel); `Film::frame_tick` saturates and
`Reel::frame_tick` uses checked arithmetic.

tests/extension_duration_limits.rs rewrites a committed vector of each format to declare
exactly its limit (it reads) and one past it (refused by name), and checks the film
schedule ending at 2^64 - 1 (reads) and past it (refused). Falsified: with the readers
as they were, all three tests fail. The world, audio, haptic, splat4d, score and film
corpora verify unchanged; the 275 lib tests with the container features pass. CI runs
the test beside container_reservations on every leg.

SECURITY.md drops the gap and keeps the one this exposes: replaying a world to a far
tick costs time in proportion (memory is bounded, time is not).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:07:16 -04:00

109 lines
4.4 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.

# WAI Extension: Haptics
> Status: Draft. Phase 2 of the cargo roadmap. Touch as instructions:
> ship a keyframed intensity/sharpness envelope, the sink reconstructs an
> actuator signal. Conformance is **sample-equivalence**. Reference impl:
> the `haptic` feature of `wai-rs`; corpus: `haptic-conformance/`; live
> sink: wai.transaction.science/haptics.
Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.
## 1. Scope and model
A `wai.haptic.signal` object is a per-channel keyframed envelope of
**intensity** and **sharpness** (the Core-Haptics-shaped pair every
modern actuator API maps to) that the sink reconstructs to a per-sample
buffer and plays on whatever actuators it has. It is lever 3
(instructions-at-the-sink) for touch — an effect is a few hundred bytes,
not a recorded waveform.
In scope: the container, the deterministic reconstruct contract, the
**sample-equivalence** conformance criterion, the fallback floor, the
receipt profile.
Out of scope: a transport; actuator hardware specifics (the
channel→actuator binding is the sink's); carrier synthesis (the signal
is an amplitude/sharpness envelope, not a modulated waveform — the
deterministic floor stays trig-free).
Relationship to the core spec: mirrors interactive-worlds / spatial
audio. The reconstruct runs in the `wai.det.fixed64` floor; `media`
takes the informational value `"haptic"`; `wai.haptic.signal` is
authoritative.
## 2. Container (`WHAP`)
```
"WHAP" | u16 n_sections | section table (u8 kind, u32 off, u32 len) | sections
```
| kind | section | required |
|---|---|---|
| `0x01` | contract (canonical JSON) | REQUIRED |
| `0x02` | channel signals | REQUIRED |
### 2.1 Contract
```json
{ "sample_rate": 1000, "numeric": "wai.det.fixed64",
"channels": 1, "duration": 1500 }
```
Haptic rates are low (≈1 kHz); `duration` is in samples.
**Reader limit.** A reader MUST NOT refuse a signal for its length while `duration ×
channels` is at most 134 217 728 (2²⁷, the audio limit of SPEC §2), and MAY refuse one
past it (`duration_too_long`), when it reads the contract, before it reconstructs
anything. A conforming encoder writes no signal past it.
### 2.2 Channel signals
`n_channels u32`, then per channel: `n_keys u16`, then
`n × ( pos_sample u64 | intensity i64 Fx | sharpness i64 Fx )`.
Both fields are in `[0, 1]`. Between keyframes they linear-interpolate
in `Fx`; before the first / after the last key they hold.
## 3. Reconstruct contract
For each channel, for each sample `t ∈ [0, duration)`:
`intensity(t)`, `sharpness(t)` = the `Fx` linear interpolation of the
channel's keyframes at `t`, each clamped to `[0,1]` and quantized to a
`u8` (`round(v · 255)`, half away from zero, in integer space). The
canonical reconstruction is, per channel, the interleaved
`[intensity, sharpness]` `u8` pairs.
## 4. Conformance — sample-equivalence
> Given the same `wai.haptic.signal`, a conforming sink MUST reconstruct
> the identical canonical `u8` buffer, hence the identical BLAKE3 — on
> every machine, no tolerance parameter.
```
signal_hash = BLAKE3("wai:haptic-signal\x01" || per-channel u8 pairs)
```
## 5. Fallback floor
A sink with no actuators ignores the signal (haptics are
supplementary); there is no perceptual substitute to fall back *to*, so
`fallback` is typically `null`. A sink MAY surface the envelope visually
(a waveform) — presentation, not conformance.
## 6. Receipt (JWP profile)
`signal_hash` is the identity; the group's objects are per-channel
reconstruction blocks, Merkle-bound; one Ed25519 signature (verified as
SPEC §7 *Signatures* requires) over signal_hash + root + `work` (= duration × channels) + carried
`joules_micro`; `parent_receipt_hash` chains — the worlds/audio receipt
shape reused. 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; present, it is inside the
signature. The reference meter labels every figure it measures
`OnChipCounter`, with its declared uncertainty.
## Appendix
Sample-equivalence is replay/mixdown-equivalence with the tick replaced
by the haptic sample. The envelope is trig-free by construction
(amplitude + sharpness, not a modulated carrier), so the `Fx` floor
reconstructs it exactly with `+,−,×,÷` alone — the simplest cargo class
to make bit-exact, which is why it's the fast Phase-2 win.