open-standards/wai/wai-web/demo/verify_generate_wasm.mjs
Transaction Science 4c09bea76a
All checks were successful
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 4s
no-vanity-metrics / WAI public text names no comparison (pull_request) Successful in 4s
no-vanity-metrics / WAI public text names no company or product (pull_request) Successful in 3s
standards workspaces build + test / arl-rs (pull_request) Successful in 1m9s
standards workspaces build + test / eoc-rs (pull_request) Successful in 1m58s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m29s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 1m10s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 1m12s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m14s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m19s
standards workspaces build + test / sense-rs (pull_request) Successful in 1m7s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 3m46s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 7m17s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 14s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 14m19s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 9s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 25s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 48s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 1m54s
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Successful in 1m7s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 1m5s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m13s
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 4s
WAI section references / cited sections exist (pull_request) Successful in 10s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 4m41s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 3m0s
workflow hygiene / every workflow supersedes its own queue (pull_request) Successful in 4s
WAI HTTP delivery / HTTP corpus · falsification and differential (pull_request) Successful in 1h29m35s
wai-pack / wai-pack · linux (pull_request) Successful in 1m8s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 22s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m19s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 1m49s
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 36m4s
standards workspaces build + test / aac-rs (pull_request) Successful in 10m19s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 9m39s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 5m5s
standards workspaces build + test / map-rs (pull_request) Successful in 5m26s
standards workspaces build + test / mesh-rs (pull_request) Successful in 5m22s
standards workspaces build + test / openpay (pull_request) Successful in 25m11s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 10m47s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 2m42s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 5m40s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 3m48s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 2m54s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 6m47s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 10m43s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 29s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 3m16s
wai-pack / wai-pack · macos (pull_request) Successful in 2m12s
wai-web wasm32 CI: the browser generator and score synth make every native pin
The CHANGELOG said every pin equals the shared browser binding's output, and the
browser sink's form-v0 reading has no CI of its own, yet wai-web-wasm.yml ran no
generate or score check. demo/verify_generate_wasm.mjs runs generateSpectral at
every size the generate vectors pin (sizes.json, instr-v0.json's four listed sizes,
and the first listed size of each object at a bound of form v0), checks each
object names the committed model, and runs ScoreSink over every score-conformance
vector for its pcm_sha256, mixdown hash and score hash. The workflow runs it on the
node build and now also triggers on wai/score-conformance/. 35 checks pass locally
on a wasm32 build of this tree.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 17:17:06 -04:00

83 lines
4 KiB
JavaScript

// The wasm32 generator and score synth equal the native pins. A page that makes a
// generated picture or a score's samples in the browser checks every byte it made
// against the SHA-256 the native reference wrote, so the binding it loads must make
// exactly those bytes:
//
// - generateSpectral at every size tests/vectors/generate/ pins — sizes.json's two
// sizes, the four listed sizes of instr-v0.json's object, and the first listed size
// of every object instr-v0.json lists as accepted (one at each bound of form v0) —
// gives the SHA-256 the vector states, made natively by `wai generate`; each
// object names the committed model by its SHA-256, checked here;
// - ScoreSink over every score-conformance vector gives the SHA-256 of its samples
// (pcm_sha256: the i16 mixdown, little-endian, SPEC §7 Digests), its mixdown hash
// and its score hash as expected_hashes.json states them, and the contract's
// duration in samples.
//
// node verify_generate_wasm.mjs [path/to/wai_web.js] [path/to/wai-rs/tests/vectors/generate] [path/to/score-conformance]
//
// Exit 0 iff every check passes.
import { readFileSync } from 'node:fs';
import { createHash } from 'node:crypto';
import { dirname, resolve } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
const here = dirname(fileURLToPath(import.meta.url));
const pkg = resolve(here, process.argv[2] ?? './pkg-node/wai_web.js');
const gen = resolve(process.argv[3] ?? resolve(here, '../../wai-rs/tests/vectors/generate'));
const scores = resolve(process.argv[4] ?? resolve(here, '../../score-conformance'));
const wai = await import(pathToFileURL(pkg).href);
const sha = (b) => createHash('sha256').update(b).digest('hex');
const json = (p) => JSON.parse(readFileSync(p, 'utf8'));
const model = new Uint8Array(readFileSync(`${gen}/generate_spectral_model.json`));
const modelSha = sha(model);
const checks = [];
const picture = (name, o, w, h, want) => checks.push([`${name} ${w}x${h}`, () => {
const img = wai.generateSpectral(model, o.prompt, o.seed, o.steps, w, h);
const rgb = img.rgb;
img.free();
return rgb.length === w * h * 3 ? sha(rgb) : `${rgb.length} bytes`;
}, want]);
const named = (name, o) => checks.push([`${name}: names the committed model`, () => o.model, `sha256:${modelSha}`]);
const sizes = json(`${gen}/sizes.json`);
checks.push(['sizes.json: the committed model', () => sizes.model_sha256, modelSha]);
for (const s of sizes.sizes) picture('sizes.json', sizes, s.w, s.h, s.sha256);
const v = json(`${gen}/instr-v0.json`);
const object = JSON.parse(v.object);
named('instr-v0.json', object);
for (const e of v.outputs) picture('instr-v0.json', object, e.w, e.h, e.sha256);
if (!(v.accepted?.length >= 12)) checks.push(['instr-v0.json lists an accepted object at each bound', () => v.accepted?.length ?? 0, '>= 12']);
for (const c of v.accepted ?? []) {
const o = JSON.parse(c.object);
named(`accepted (${c.why})`, o);
for (const e of c.outputs) picture(`accepted (${c.why})`, o, e.w, e.h, e.sha256);
}
const expected = json(`${scores}/expected_hashes.json`);
for (const [file, want] of Object.entries(expected)) {
checks.push([`score ${file}`, () => {
const s = new wai.ScoreSink(new Uint8Array(readFileSync(`${scores}/vectors/${file}`)));
const pcm = s.mixdown();
const le = new Uint8Array(2 * pcm.length);
const dv = new DataView(le.buffer);
pcm.forEach((x, i) => dv.setInt16(2 * i, x, true));
const got = [pcm.length, sha(le), s.mixdown_hash_hex(), s.score_hash_hex()].join(' ');
s.free();
return got;
}, [want.duration, want.pcm_sha256, want.mixdown_hash, want.score_hash].join(' ')]);
}
let pass = true;
for (const [name, run, want] of checks) {
let got;
try { got = run(); } catch (e) { got = `threw: ${e?.message ?? e}`; }
const ok = got === want;
pass = pass && ok;
console.log(`${ok ? 'PASS' : 'FAIL'} ${name.padEnd(64)} ${String(got).slice(0, 16)}${ok ? '' : ` want ${want}`}`);
}
console.log(`${checks.length} checks`);
process.exit(pass ? 0 : 1);