open-standards/wai/wai-det-jpegai
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Transaction Science c2e52d1e16
Some checks failed
wai-det-jpegai WAISM float-level module of record / does this change touch it (push) Successful in 25s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Has been cancelled
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 8s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 50s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 7m10s
wai-det-jpegai WAISM float-level module of record / linux · both checkers, F1, F2 on wasmtime (Cranelift, Winch, Pulley) and Wasmi, the planted faults (push) Successful in 13m54s
wai-det-jpegai WAISM float-level module of record / linux-arm64 · both checkers, F1, F2 on wasmtime (Cranelift, Winch, Pulley) and Wasmi, the planted faults (push) Failing after 14m33s
wai-det-jpegai: the JPEG AI exact decoder as the float level's module of record, with F1 and F2 on two instruction sets
One WebAssembly module with the interface of waism §5 and nothing else, decoding
wai.image.jpegai through wai-rs's exact decoder, the integer weight bundle arriving as the set
(the file sequence of norm.json and weights/*.json). Refusals are the capability's codes; a
codestream outside the decoder's configuration traps. Both checkers hold it at the float level
and refuse it R3 at the integer level. record/ keeps the module of record, built on Linux
x86_64 over main at 373fe3594 and named by its SHA-256 (53d54e58…, 666,425 bytes).

corpus.json, written by corpus.py from committed files: the reference codestream over the
pinned jpegai/v1 bundle, to the native exact decoder's pinned RGB8 (dbc494a5…), and three
refusal fixtures. host/ runs the probe on each engine first, in the same process (§6 check 10),
then every case on an instance of its own: wasmtime 50.0.1 with Cranelift, Winch and Pulley,
each with its NaN canonicalisation on and off, and Wasmi 2.0.0 built with `deterministic`.

What ran (rb, slot limiter):
- Linux x86_64 (Framework): F1 holds, the probe's bytes on the four engines with the NaN rule
  set and other bytes on the three without it; the planted probe fails F1. F2: the module of
  record gives every case the corpus's on all four engines, one run digest c7025bbc….
- Linux arm64 (Thor): build.sh with both checkers; F1 holds and the planted probe fails it; F2
  on the module of record and on this host's own build (ffb2c94f…, other bytes): eight runs,
  the same run digest. module-check.sh in full passes.
- The planted decoder (feature planted-nan-branch) gives the pinned RGB8 on the four engines
  the probe holds, another output on the three with the rule unset run past the probe, and the
  probe refuses those engines first (module_probe_mismatch), on both hosts.
- wai registry export --check, wai ops export --check and wai breaking against main: clean.

module-check.sh holds record/ to its manifest and both verdicts; wai-det-jpegai.yml runs the
build, F1, F2 and the planted faults on linux and linux-arm64.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 18:37:15 -05:00
..
host wai-det-jpegai: the JPEG AI exact decoder as the float level's module of record, with F1 and F2 on two instruction sets 2026-11-09 18:37:15 -05:00
record wai-det-jpegai: the JPEG AI exact decoder as the float level's module of record, with F1 and F2 on two instruction sets 2026-11-09 18:37:15 -05:00
src wai-det-jpegai: the JPEG AI exact decoder as the float level's module of record, with F1 and F2 on two instruction sets 2026-11-09 18:37:15 -05:00
build.rs wai-det-jpegai: the JPEG AI exact decoder as the float level's module of record, with F1 and F2 on two instruction sets 2026-11-09 18:37:15 -05:00
build.sh wai-det-jpegai: the JPEG AI exact decoder as the float level's module of record, with F1 and F2 on two instruction sets 2026-11-09 18:37:15 -05:00
Cargo.lock wai-det-jpegai: the JPEG AI exact decoder as the float level's module of record, with F1 and F2 on two instruction sets 2026-11-09 18:37:15 -05:00
Cargo.toml wai-det-jpegai: the JPEG AI exact decoder as the float level's module of record, with F1 and F2 on two instruction sets 2026-11-09 18:37:15 -05:00
corpus.json wai-det-jpegai: the JPEG AI exact decoder as the float level's module of record, with F1 and F2 on two instruction sets 2026-11-09 18:37:15 -05:00
corpus.py wai-det-jpegai: the JPEG AI exact decoder as the float level's module of record, with F1 and F2 on two instruction sets 2026-11-09 18:37:15 -05:00
README.md wai-det-jpegai: the JPEG AI exact decoder as the float level's module of record, with F1 and F2 on two instruction sets 2026-11-09 18:37:15 -05:00

wai-det-jpegai — the JPEG AI exact decoder at the float level

The module of record of WA²ISM™'s float level (extensions/waism.md, waism/1-det): one WebAssembly module, with the interface of waism §5 and nothing else, that decodes wai.image.jpegai through wai-rs's exact decoder (jpegai_decode). That is the decode a native sink runs when it holds the capability's integer weight bundle: the integer entropy decode and scale path, the integer networks through the integer operator set (jpegai-int/1), then dequantisation, latent refinement, the post-filters and colour conversion in IEEE-754 float, in the fixed order of the source. The bundle is not in the module: it arrives as the capability's parameter set.

capability payload set output
wai.image.jpegai the codestream the file sequence of norm.json and weights/*.json RGB8, rows top to bottom

A refusal is the capability's own code, with wai_run returning 1: set_incomplete, model_mismatch, stream_exhausted or field_range (SPEC §5, the wai.image.jpegai row). A codestream outside the one configuration the decoder implements has no code in that row: the module traps, and the sink refuses the decode through it (module_trap).

What holds it

  • Held at the float level, refused at the integer level. Floating point is in the module, so both of the profile's checkers (../wai-module-check), the Rust checker and check-module.mjs, give it held at the float level and refused R3 at the integer level, the same verdict from each. No import, one memory with a declared maximum (8,192 pages; a decode of the reference codestream reaches 3,964), the six exports of §5. Every allocation traps where memory cannot grow, so a failed memory.grow inside wai_run ends in a trap and never in an output (R13); wai_alloc alone returns 0.
  • The probe first. At the float level a sink states a tier for the output only on an engine set to the deterministic profile's NaN rule, shown by the probe of waism §9.3 run in the same process before the module's first run there (§6, check 10). The probe is ../wai-module-check/planted/probe.wasm, and the bytes it must give are the bytes line of probe.txt beside it: the positive canonical NaN in every lane of the twelve cases. planted/build.py writes both, and both checkers hold the probe at the float level.
  • The module corpus. corpus.json, written by corpus.py: the reference codestream (wai-rs/tests/vectors/jpegai/test256.bin) over the pinned jpegai/v1 bundle, to the SHA-256 of the RGB8 the native exact decoder gives (wai-rs/tests/jpegai_cross_platform.rs, PINNED's rgb), and three refusal fixtures: the bundle without weights/icci.json (set_incomplete), and with norm.json or weights/icci.json not JSON (model_mismatch). The bundle's files are named under external, with their SHA-256 from tools/jpegai-e2e-v1.sha256; tools/wai_jpegai_fetch_e2e_inputs.sh fetches them and checks each. The module contains memory.grow, so §9.2 also gives it grow-refusal cases; corpus.json does not hold them, and no run here makes a memory.grow fail.
  • The engines. host/ runs the probe on each engine, then every case on an instance of its own, as waism §5 and §6 checks 10 to 13 state, holds every output to the corpus, and prints a run digest: the SHA-256 of one line a case, its name and its outcome. Each engine is set as waism §7 sets one at the float level: threads-off, relaxed-off, fuel, the memory limited to its declared maximum, a fixed stack, and nan-canonical.
engine how its NaN rule is set
[email protected]/cranelift Config::cranelift_nan_canonicalization(true)
[email protected]/winch the same setting, which Winch honours
[email protected]/pulley Cranelift compiling to Pulley, wasmtime's portable bytecode, run by its interpreter, with the same setting
[email protected] the crate feature deterministic, which canonicalises every NaN a floating-point instruction gives

F1 and F2

  • F1: the probe tells the engines apart. On wasmtime with Cranelift, with Winch and with Pulley, the probe gives its bytes with the NaN rule set and other bytes with it unset; on Wasmi built with its rule, its bytes. host --f1 runs it on all seven and holds them to that.
  • F2: the decode is the native exact decoder's on every engine the probe holds. The module of record decodes the corpus's vector to the native exact decoder's pinned RGB8 (dbc494a5…), and gives each refusal fixture its code, on the four engines above, on two instruction sets.

Each host ran host --f1, then host --external J corpus.json over the module of record and its own build of this source, the probe first on each engine in the same process:

host F1: the probe's bytes, the NaN rule set F1: other bytes, the rule unset F2: every case the corpus's
Linux x86_64 Cranelift, Winch, Pulley, Wasmi Cranelift and Winch in all twelve cases, Pulley in eleven Cranelift, Winch, Pulley, Wasmi
Linux arm64 Cranelift, Winch, Pulley, Wasmi Cranelift and Winch in nine cases, Pulley in eight Cranelift, Winch, Pulley, Wasmi

Every F2 run gave one run digest, c7025bbcd5448f1ba2f720c80f60fefa14c594f9f28942f08b2a96c5abfcae12. With the rule unset, a case on a NaN operand (1, 2 and 6 to 12) keeps that NaN's sign, quieted, on both instruction sets, except f32.nearest (case 7) on Pulley, which gives the canonical NaN; a NaN made from numbers (cases 3 to 5, and lane 3 of case 11) is negative on Linux x86_64 and the canonical one on Linux arm64, whose hardware makes that NaN.

The module of record, run past the probe (--no-probe) on the three engines with the rule unset, gave every case the corpus's on both hosts too: no NaN its decode of this corpus makes reaches its output. A sink does not rest on that. At the float level the output holds a tier only on an engine the probe holds (§6, check 10), and then the tier §2 gives that level.

The module of record

record/ holds one build of this crate for wasm32-unknown-unknown, named by its SHA-256 in record/manifest.json: wai_det_jpegai.wasm, 666,425 bytes, 53d54e58dca9e69bfa0aa8b88b5bb95b05fb9d70389bbf55924eb6d924dd758c, built on Linux x86_64. The module is the same source on every host, and its bytes are not (../wai-int-module says why): Linux arm64's build of this source is 666,221 bytes (ffb2c94f…), and gave the same run digest on every engine there. So record/ is named by its SHA-256, and a build is held to the corpus, not to record/'s bytes.

The planted faults

Each check is shown failing on the fault it exists for.

  • A probe with a NaN-dependent branch fails F1. probe-nan-branch.wasm is the probe with a branch after each operation on whether its result is a NaN, writing the canonical NaN itself. It meets R1 to R12, and both checkers hold it at the float level, but it gives the probe's bytes on every engine, the ones with the NaN rule unset among them: host --f1 --probe planted/probe-nan-branch.wasm reports that it accepted an engine whose NaN rule is not set, and fails.
  • A decoder with a NaN-dependent branch departs from the pinned output where the probe refuses the engine. The feature planted-nan-branch (build.sh --planted) adds to the decode a branch on the sign of a NaN made at run time from a negative signalling NaN, which flips one bit of the output when the sign is set. The deterministic profile makes that NaN the positive canonical NaN, so on the four engines above the planted module still decodes to the pinned RGB8. On the same wasmtime engines with the NaN rule unset, run past check 10 (--no-probe), its output is another; with check 10 in place, the probe refuses those engines (module_probe_mismatch) and the module never runs there.

Building and running

bash build.sh [--out DIR] [--planted]            # the module, held by both checkers
bash ../tools/wai_jpegai_fetch_e2e_inputs.sh J   # the jpegai/v1 bundle, each file checked
(cd host && cargo build --locked --release)
host/target/release/wai-det-jpegai-host --f1
host/target/release/wai-det-jpegai-host --f1 --probe ../wai-module-check/planted/probe-nan-branch.wasm
host/target/release/wai-det-jpegai-host --external J corpus.json record/wai_det_jpegai.wasm
python3 -I corpus.py --check                     # corpus.json is what corpus.py writes

build.sh names no path of the machine that runs it in the module: the checkout, the Cargo home and the toolchain are remapped.

CI: .github/wai-conformance/verifiers/module-check.sh holds record/ to its manifest, the module of record at the float level and refused R3 at the integer level by both checkers, and the corpus to corpus.py; .github/workflows/wai-det-jpegai.yml runs the build, F1, the engines and the planted faults on Linux x86_64 and Linux arm64.