All checks were successful
joule-moq-remote host agent / joule-moq-remote · does this change touch it (pull_request) Successful in 6s
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Successful in 7s
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 6s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 6s
wai-app and Create / wai-app · does this change touch it (pull_request) Successful in 6s
wai-audit collector and player integration / does this change touch it (pull_request) Successful in 6s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (pull_request) Successful in 6s
WAI operation catalogue / does this change touch it (pull_request) Successful in 6s
WAI conformance / does this change touch it (pull_request) Successful in 6s
WAI energy receipts / does this change touch it (pull_request) Successful in 5s
WAI examples / WAI examples · does this change touch them (pull_request) Successful in 6s
wai-fleet service / does this change touch it (pull_request) Successful in 6s
WAI JPEG-AI whole decode (pinned inputs, required) / does this change touch it (pull_request) Successful in 7s
wai-ledger service / does this change touch it (pull_request) Successful in 6s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (pull_request) Successful in 1m25s
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 6s
wai-quantum for JavaScript / does this change touch it (pull_request) Successful in 6s
wai-quantum studio and desktop / does this change touch it (pull_request) Successful in 6s
wai-sink-android native sink library / does this change touch it (pull_request) Successful in 6s
wai-sink-swift native sink package / does this change touch it (pull_request) Successful in 6s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (pull_request) Successful in 1m57s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 2m7s
WAI Studio / does this change touch it (pull_request) Successful in 6s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 6s
wai-web wasm32 / does this change touch it (pull_request) Successful in 6s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (pull_request) Successful in 29s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (pull_request) Successful in 2m1s
WAI conformance / fast integer kernels, every x86_64 kernel set (AVX-512 silicon) (pull_request) Successful in 2m59s
wai-audit collector and player integration / collector · linux (pull_request) Successful in 1m1s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 26s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 4m19s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 16s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 1m4s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (pull_request) Successful in 1m15s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (pull_request) Successful in 5m15s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 6m19s
wai-fleet service / service · macos (pull_request) Successful in 1m2s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (pull_request) Successful in 2m46s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 1m43s
wai-ledger service / service · macos (pull_request) Successful in 1m24s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 1m2s
WAI operation catalogue / the catalogue and every door · linux (pull_request) Successful in 4m18s
wai-quantum studio and desktop / desktop app · windows (pull_request) Successful in 9m19s
wai-pack / wai-pack · macos (pull_request) Successful in 3m8s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 30s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 7m49s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 3m23s
WAI conformance / faces from keypoints under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m39s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 3m9s
wai-fleet service / service · linux (pull_request) Successful in 2m5s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 3m28s
wai-ledger service / service · linux (pull_request) Successful in 1m43s
wai-pack / wai-pack · linux (pull_request) Successful in 1m55s
wai-quantum for JavaScript / javascript · linux (pull_request) Successful in 3m0s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (pull_request) Successful in 1m10s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 16m12s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 11m32s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Successful in 4m51s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (pull_request) Successful in 13m12s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 15m2s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 4m3s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 16m34s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 2m40s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 17m22s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m44s
wai-quantum studio and desktop / desktop app · linux (pull_request) Successful in 5m46s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 34s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 17s
WAI examples / WAI examples · portable · linux (pull_request) Successful in 9m28s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 5m7s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 14m49s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 4m34s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 2m16s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 11m4s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 13m6s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 5m58s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Successful in 10m55s
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Successful in 9m51s
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 10m15s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 9m21s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 30m14s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 1h4m7s
Every change to WAI added an entry at the head of wai/CHANGELOG.md, and every new module appended to the hand-kept lists of .github/workflows/wai-conformance.yml. So any two branches conflicted on those lines and had to merge one after another. This moves both into files a change adds. - Changelog entries: a change adds wai/changes/<yyyy-mm-dd>-<slug>.md holding the entry text. scripts/changelog-assemble.py renders the entries into CHANGELOG.md's Unreleased section for a release, in a stable order. scripts/check-changelog-fragment.py, run by repo-checks on every pull request, fails a pull request that changes wai/ and adds or edits no entry file directly in wai/changes/ (a release passes, and so does a commit with the trailer line "Changelog: none"). A pull request opened before the check reached main passes as it is; one opened after it that adds its entry to the head of Unreleased fails and is told where the entry goes. wai breaking reads breaking changes from entry files too. - The conformance workflow: each per-module verifier step is a file in .github/wai-conformance/verifiers/, each corpus a file in corpora/, and the integer-kernel tests carry a tag line. scripts/wai-conformance-lists.py derives every list the workflow kept by hand (WAI_FEATURES, the union --bin and --test lists, the features-alone lists, the kernel and corpus steps) from those files when the workflow runs. Its `selftest --exact` holds the derived lists to the hand lists of the commit before this one, and its dry-run prints every command each job runs: the dry-runs before and after this change differ only in the gate's path list, which gains the two paths this change adds. - What stays shared, and how to insert there, is written in wai/CONTRIBUTING.md and wai/changes/README.md: wai-rs Cargo.toml features and [[test]] entries, lib.rs, registry rows, wai/.gitattributes and the live corpus table take an insert beside the related entry, never at the end. Co-Authored-By: Claude Opus 5.5 <[email protected]>
172 lines
8 KiB
Rust
172 lines
8 KiB
Rust
// wai-conformance: int-kernels-release
|
|
//! The reference readers build a manifest with `container::read_manifest` and
|
|
//! `read_manifest_v2`, which move its members out of one JSON tree (SPEC §2, *Reader
|
|
//! limits*: so a parse allocates within a bound), where they used serde's derived reader
|
|
//! (`serde_json::from_slice::<Manifest>`). This test holds the two to the same result:
|
|
//! for the manifest of every envelope committed under `wai/`, and for each of them with
|
|
//! every member, at the manifest's top level and one level down, removed or given a
|
|
//! value of each JSON type, both readers read it or both refuse it, and what they read
|
|
//! writes back to the same JSON.
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use serde_json::{json, Value};
|
|
use wai::container::{check_unique_members, manifest_bytes, read_manifest, read_manifest_v2, Manifest, ManifestV2};
|
|
|
|
fn envelopes(dir: &Path, out: &mut Vec<PathBuf>) {
|
|
let Ok(rd) = std::fs::read_dir(dir) else { return };
|
|
let mut entries: Vec<_> = rd.flatten().map(|e| e.path()).collect();
|
|
entries.sort();
|
|
for p in entries {
|
|
let name = p.file_name().and_then(|n| n.to_str()).unwrap_or("");
|
|
if p.is_dir() {
|
|
if name != "target" && name != "node_modules" && !name.starts_with('.') {
|
|
envelopes(&p, out);
|
|
}
|
|
} else if name.ends_with(".wai") {
|
|
out.push(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Both readers on `text`: the same verdict, and the same JSON when both read it.
|
|
fn same(text: &[u8], v2: bool, what: &str) -> bool {
|
|
let (derived, moved) = if v2 {
|
|
let d = check_unique_members(text).and_then(|_| serde_json::from_slice::<ManifestV2>(text)).map(|m| serde_json::to_string(&m).unwrap());
|
|
let m = read_manifest_v2(text).map(|m| serde_json::to_string(&m).unwrap());
|
|
(d, m)
|
|
} else {
|
|
let d = check_unique_members(text).and_then(|_| serde_json::from_slice::<Manifest>(text)).map(|m| serde_json::to_string(&m).unwrap());
|
|
let m = read_manifest(text).map(|m| serde_json::to_string(&m).unwrap());
|
|
(d, m)
|
|
};
|
|
match (&derived, &moved) {
|
|
(Ok(a), Ok(b)) => assert_eq!(a, b, "{what}: the readers read different manifests"),
|
|
(Err(_), Err(_)) => {}
|
|
_ => panic!("{what}: the derived reader gives {derived:?}, the reference reader {moved:?}"),
|
|
}
|
|
moved.is_ok()
|
|
}
|
|
|
|
/// The values a member is given: one of each JSON type, and a few of each kind the
|
|
/// manifest types read (a string, an integer, a float, `null`).
|
|
fn replacements() -> Vec<Value> {
|
|
vec![json!(null), json!(true), json!(0), json!(-1), json!(1.5), json!(18446744073709551615u64), json!("s"), json!([]), json!({}), json!([{}]), json!({"x": 1})]
|
|
}
|
|
|
|
fn mutations(text: &[u8], v2: bool, what: &str) -> usize {
|
|
let Ok(Value::Object(top)) = serde_json::from_slice::<Value>(text) else { return 0 };
|
|
let mut n = 0;
|
|
let mut check = |m: &serde_json::Map<String, Value>, why: String| {
|
|
same(serde_json::to_string(m).unwrap().as_bytes(), v2, &format!("{what}: {why}"));
|
|
n += 1;
|
|
};
|
|
for (k, v) in &top {
|
|
let mut m = top.clone();
|
|
m.shift_remove(k);
|
|
check(&m, format!("without {k}"));
|
|
for r in replacements() {
|
|
let mut m = top.clone();
|
|
m.insert(k.clone(), r.clone());
|
|
check(&m, format!("{k} = {r}"));
|
|
}
|
|
// One level down: each member of an object member, and of the first object of
|
|
// an array member (a WAI2's first rendition entry).
|
|
let (inner, at): (Option<&serde_json::Map<String, Value>>, Option<usize>) = match v {
|
|
Value::Object(o) => (Some(o), None),
|
|
Value::Array(a) => match a.first() {
|
|
Some(Value::Object(o)) => (Some(o), Some(0)),
|
|
_ => (None, None),
|
|
},
|
|
_ => (None, None),
|
|
};
|
|
let Some(inner) = inner else { continue };
|
|
let put = |o: serde_json::Map<String, Value>| {
|
|
let mut m = top.clone();
|
|
let nv = match at {
|
|
None => Value::Object(o),
|
|
Some(i) => {
|
|
let mut a = v.as_array().unwrap().clone();
|
|
a[i] = Value::Object(o);
|
|
Value::Array(a)
|
|
}
|
|
};
|
|
m.insert(k.clone(), nv);
|
|
m
|
|
};
|
|
for ik in inner.keys() {
|
|
let mut o = inner.clone();
|
|
o.shift_remove(ik);
|
|
check(&put(o), format!("{k} without {ik}"));
|
|
for r in replacements() {
|
|
let mut o = inner.clone();
|
|
o.insert(ik.clone(), r.clone());
|
|
check(&put(o), format!("{k}.{ik} = {r}"));
|
|
}
|
|
}
|
|
}
|
|
n
|
|
}
|
|
|
|
#[test]
|
|
fn the_reference_reader_reads_every_committed_manifest_as_the_derived_reader_does() {
|
|
let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("..");
|
|
let mut files = Vec::new();
|
|
envelopes(&root, &mut files);
|
|
assert!(files.len() >= 400, "found only {} envelopes under {}", files.len(), root.display());
|
|
let (mut read, mut refused, mut mutated) = (0, 0, 0);
|
|
// Mutate the manifests of each distinct set of top-level members once.
|
|
let mut shapes = std::collections::BTreeSet::new();
|
|
for f in &files {
|
|
let b = std::fs::read(f).unwrap();
|
|
let Ok(text) = manifest_bytes(&b) else { continue };
|
|
let v2 = &b[..4] == b"WAI2";
|
|
let what = f.strip_prefix(&root).unwrap_or(f).display().to_string();
|
|
if same(text, v2, &what) {
|
|
read += 1;
|
|
} else {
|
|
refused += 1;
|
|
}
|
|
if let Ok(Value::Object(o)) = serde_json::from_slice::<Value>(text) {
|
|
let keys: Vec<String> = o.keys().cloned().collect();
|
|
if shapes.insert((v2, keys)) {
|
|
mutated += mutations(text, v2, &what);
|
|
}
|
|
}
|
|
}
|
|
eprintln!("{} envelopes: {read} manifests read alike, {refused} refused alike; {mutated} mutations agree", files.len());
|
|
assert!(read >= 300, "only {read} manifests read");
|
|
assert!(mutated >= 1000, "only {mutated} mutations");
|
|
}
|
|
|
|
/// The cases the derived reader treats specially, each read alike: a member whose value
|
|
/// is `null` where an `Option` is (`None`, not written back), `target` absent (`null`),
|
|
/// a member no revision defines kept in order, a float `min_sim`, and an integer past
|
|
/// `u64` as `kid`.
|
|
#[test]
|
|
fn the_derived_readers_special_cases_are_read_alike() {
|
|
let base = r#"{"wai":"1.0","media":"image","intent":"replicate","model_requirement":{"capability":"wai.image.png","fallback":null,"min_sim":0.5,"z":1,"a":[1]},"conditioning":{"kind":"png","prior":null,"q":{}},"security":null,"refines":null,"derived_from":null,"later":{"b":2},"earlier":3}"#;
|
|
for t in [
|
|
base.to_string(),
|
|
base.replace(r#""min_sim":0.5"#, r#""min_sim":1"#),
|
|
base.replace(r#""security":null"#, r#""security":{"encryption":"none","kid":18446744073709551616}"#),
|
|
base.replace(r#""security":null"#, r#""security":{"kid":-1}"#),
|
|
base.replace(r#""conditioning":{"kind":"png","prior":null,"q":{}}"#, r#""conditioning":{"kind":"png","prior":{"id":"x","sha256":"y","determinism":"z","supersedes":null,"k":[]}}"#),
|
|
base.replace(r#""conditioning":{"kind":"png","prior":null,"q":{}}"#, r#""conditioning":{"kind":"png","prior":{"id":"x","sha256":"y","determinism":"z","supersedes":{"sha256":"w","form":null}}}"#),
|
|
base.replace(r#""conditioning":{"kind":"png","prior":null,"q":{}}"#, r#""conditioning":{"kind":"png","prior":{"id":"x","sha256":"y"}}"#),
|
|
"[]".into(),
|
|
"null".into(),
|
|
"{}".into(),
|
|
] {
|
|
same(t.as_bytes(), false, &t);
|
|
}
|
|
let v2 = r#"{"wai":"1.1","media":"text","intent":"replicate","renditions":[{"capability":"wai.text.zstd","kind":"zstd","refines":null,"envelope_manifest":{"a":1},"x":[]}],"security":{"encryption":"none"},"z":1}"#;
|
|
for t in [
|
|
v2.to_string(),
|
|
v2.replace(r#""renditions":[{"#, r#""renditions":[1,{"#),
|
|
v2.replace(r#""renditions":["#, r#""renditions":{"a":"#).replace(r#""x":[]}],"#, r#""x":[]}},"#),
|
|
v2.replace(r#""kind":"zstd","#, ""),
|
|
] {
|
|
same(t.as_bytes(), true, &t);
|
|
}
|
|
}
|