Some checks failed
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 11s
wai-quantum energy-landscape jobs / does this change touch it (push) Successful in 14s
joule-code conformance gate / joule-moq-relay, its own energy on a counter (macos) (push) Successful in 1m8s
wai-quantum energy-landscape jobs / the module under wasm32-wasip2 (wasmtime) (push) Successful in 2m30s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 2m53s
joule-code conformance gate / Build + round-trip C1-C4 vectors (push) Successful in 4m4s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Failing after 4m26s
wai-quantum energy-landscape jobs / the same bits · macos (push) Successful in 4m5s
wai-quantum energy-landscape jobs / native against WebAssembly, over many seeds (push) Successful in 5m14s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 5m51s
joule-code conformance gate / joule-moq-relay, every target (push) Successful in 7m59s
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (push) Successful in 7m41s
wai-quantum energy-landscape jobs / reader, kernels, answers and the stated result · linux, release (push) Successful in 7m57s
joule-moq-remote host agent / joule-moq-remote · does this change touch it (push) Has been cancelled
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (push) Has been cancelled
joule-moq-remote host agent / joule-moq-remote, every target (macos) (push) Has been cancelled
joule-code conformance gate / joule-moq-ingest, every target (push) Successful in 8m58s
joule-moq-room room service / joule-moq-room · does this change touch it (push) Has been cancelled
joule-moq-room room service / joule-moq-room, every target (push) Has been cancelled
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Has been cancelled
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Has been cancelled
wai-app and Create / wai-app · does this change touch it (push) Has been cancelled
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Has been cancelled
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Has been cancelled
wai-audit collector and player integration / does this change touch it (push) Has been cancelled
wai-audit collector and player integration / collector · linux (push) Has been cancelled
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Has been cancelled
WAI operation catalogue / does this change touch it (push) Has been cancelled
WAI operation catalogue / the catalogue and every door · linux (push) Has been cancelled
wai-fleet service / does this change touch it (push) Has been cancelled
wai-fleet service / service · linux (push) Has been cancelled
wai-fleet service / service · macos (push) Has been cancelled
wai-key-proxy / does this change touch it (push) Has been cancelled
wai-key-proxy / proxy · linux (push) Has been cancelled
wai-ledger service / does this change touch it (push) Has been cancelled
wai-ledger service / service · linux (push) Has been cancelled
wai-ledger service / service · macos (push) Has been cancelled
wai-quantum for JavaScript / does this change touch it (push) Has been cancelled
wai-quantum for JavaScript / javascript · linux (push) Has been cancelled
wai-quantum studio and desktop / does this change touch it (push) Has been cancelled
wai-quantum studio and desktop / studio · browser (push) Has been cancelled
wai-quantum studio and desktop / desktop app · macos (push) Has been cancelled
wai-quantum studio and desktop / desktop app · linux (push) Has been cancelled
wai-quantum studio and desktop / desktop app · windows (push) Has been cancelled
wai-sink-android native sink library / does this change touch it (push) Has been cancelled
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Has been cancelled
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Has been cancelled
wai-sink-swift native sink package / does this change touch it (push) Has been cancelled
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Has been cancelled
WAI Studio / does this change touch it (push) Has been cancelled
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Has been cancelled
WAI Studio / Studio in Chromium and Firefox · macos (push) Has been cancelled
WAI Studio / Studio in WebKit · linux (push) Has been cancelled
wai cross-architecture byte-equality / does this change touch it (push) Has been cancelled
wai cross-architecture byte-equality / linux-aarch64 (push) Has been cancelled
wai cross-architecture byte-equality / linux-x86_64 (push) Has been cancelled
wai cross-architecture byte-equality / macos-arm64 (reference) (push) Has been cancelled
wai cross-architecture byte-equality / windows-x86_64 (push) Has been cancelled
WAI named two of the DCVC neural video codecs and none of the rest, nothing for the neural-network-based video coding exploration of ITU-T and ISO/IEC, and no lossless frame format for tensors. SPEC §5 registers wai.neural.dcvc, .dcvc_tcm, .dcvc_hem, .dcvc_dc and .dcvc_uf as candidates (neural-float, media class video, patent-pool provisionally: the code is under MIT, which grants no patent licence). Each published decoder takes its latent's entropy parameters from a temporal context as well as the hyperprior, most from a spatial prior of two or four passes too, four decode a motion latent, and DCVC-UF codes a chunk of frames as one latent; none fits the inter-frame route of neural/video_dcvc.rs, whose latent comes from the hyperprior alone, and none pins a bitstream. A new SPEC note says so, and that dcvc_rt and dcvc_fm decode through that route with a sink-supplied temporal decoder. wai.video.nnvc is a candidate too: no standard is published and the reference software's bitstream follows its release. Candidates are refused by dispatch and never picked (SPEC §7.1), which the existing candidate tests now cover for all six. wai.tensor.weft registers one frame of the WeftCodec format 1.0 (WEFTCODEC-FORMAT v1.0.0, WEFT-AI-CODEC v1.3.0, weftcodec 1.2.0) in mode RAW, TENSOR or FLOAT_TENSOR, naming no dependency: lossless, its plaintext bound by a BLAKE3 content hash, so standard-defined; royalty-free (text CC BY 4.0, reference implementation Apache-2.0). Any other mode is UnsupportedMode, as the format requires of a decoder that does not implement a mode. The reference is codecs::weft (feature weft): a decoder that checks a frame in the format's order and refuses it with the format's own class, and writers that give the format's own frames byte for byte. The corpus, tensor-conformance/, holds 77 of the format's frozen vectors with the outcome this capability gives each, and 70 generated frames, five that decode and the rest with one planted defect each, every class and every limit reached. weft_verify.py, its second implementation, agrees on all 147, refuses all 143 changed frames, and requires each of its 77 checks to refuse some vector and to be needed: 72 alone, five (always backed by another check with the same class) together with that check. staged_verify.py's video class names the DCVC candidates as the reference's does. The registry export, REGISTRY.md and wai.h are regenerated. Thirteen faults planted, each failing its tests: DCVC-HEM not a candidate, NNVC royalty-free, DCVC-UF outside the video class, ZSTD admitted, no content hash, no header CRC, a 25-bit code admitted, no exponent range, no table_len check, weft as neural-float, SPEC missing a class (both implementations), and the second verifier without its exponent range. Co-Authored-By: Claude Opus 5.5 <[email protected]>
171 lines
7.5 KiB
Python
Executable file
171 lines
7.5 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""check-wai-corpus-licences.py: every vector of a WAI corpus that keeps licence records
|
|
has one, and every record states a licence that permits redistribution.
|
|
|
|
A corpus listed in CORPORA keeps `licences.json` beside its README:
|
|
|
|
{"format": "wai-corpus-licences/1",
|
|
"vectors": {"<name>": {"licence": "<SPDX id>", "content": "generated" | "third-party",
|
|
"generator": "<path below wai/>", "input": "<what it reads>",
|
|
"source": "<where a third-party input comes from>",
|
|
"training": {"content": ..., "generator": ..., "input": ...,
|
|
"source": ..., "licence": ...}}}}
|
|
|
|
What each corpus is held to:
|
|
|
|
1. `licences.json` exists, parses, and has this format.
|
|
2. Every vector has a record: each directory under `vectors/`, and each name in the
|
|
corpus's `expected_hashes.json` and `sha256.json`, is a key of `vectors`. Every key
|
|
names a directory under `vectors/`.
|
|
3. `licence` is one of LICENCES, the licences that permit redistribution.
|
|
4. `content` "generated" names a `generator` that exists below `wai/` and a non-empty
|
|
`input`, and no `source`. `content` "third-party" names a non-empty `source`; its
|
|
`licence` is the source's, and any `generator` it names exists.
|
|
5. A vector whose `expected_hashes.json` entry has `"trained": true` has a `training`
|
|
record, held to rule 4, whose `licence`, required when its content is third-party,
|
|
is one of LICENCES; a vector not marked trained has none.
|
|
6. A record has no member other than these.
|
|
|
|
The check holds what the records state, not their truth: that is a reviewer's, and for
|
|
`neural-conformance`'s trained vector also `tools/wai_int_synth_real.py --check`'s.
|
|
|
|
check-wai-corpus-licences.py [--root DIR]
|
|
|
|
Exit 0 when every corpus holds, printing each corpus's vectors by licence; 1 with one
|
|
line a finding otherwise. Standard library only.
|
|
"""
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
# The corpora that keep a licence record for each vector, relative to the root.
|
|
CORPORA = ["wai/neural-conformance", "wai/tensor-conformance"]
|
|
|
|
FORMAT = "wai-corpus-licences/1"
|
|
|
|
# SPDX identifiers of the licences a committed vector, or the data a committed model was
|
|
# trained on, may carry: each permits redistribution.
|
|
LICENCES = {"Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "CC-BY-4.0", "CC0-1.0", "MIT"}
|
|
|
|
VECTOR_KEYS = {"licence", "content", "generator", "input", "source", "training"}
|
|
TRAINING_KEYS = {"content", "generator", "input", "source", "licence"}
|
|
|
|
|
|
def names_in(path):
|
|
"""The top-level keys of a JSON object file, or None when the file does not exist."""
|
|
if not os.path.exists(path):
|
|
return None
|
|
with open(path, encoding="utf-8") as fh:
|
|
doc = json.load(fh)
|
|
return doc if isinstance(doc, dict) else {}
|
|
|
|
|
|
def nonempty(v):
|
|
return isinstance(v, str) and v.strip() != ""
|
|
|
|
|
|
def check_content(root, where, rec, findings):
|
|
"""Rule 4, for a vector record or a training record."""
|
|
content = rec.get("content")
|
|
gen = rec.get("generator")
|
|
if gen is not None and not (nonempty(gen) and os.path.isfile(os.path.join(root, "wai", gen))):
|
|
findings.append(f"{where}: generator {gen!r} is not a file below wai/")
|
|
if content == "generated":
|
|
if gen is None:
|
|
findings.append(f"{where}: generated content names no generator")
|
|
if not nonempty(rec.get("input")):
|
|
findings.append(f"{where}: generated content names no input")
|
|
if "source" in rec:
|
|
findings.append(f"{where}: generated content has a source")
|
|
elif content == "third-party":
|
|
if not nonempty(rec.get("source")):
|
|
findings.append(f"{where}: third-party content names no source")
|
|
else:
|
|
findings.append(f"{where}: content {content!r} is neither \"generated\" nor \"third-party\"")
|
|
|
|
|
|
def check_corpus(root, corpus, findings, split):
|
|
base = os.path.join(root, corpus)
|
|
path = os.path.join(base, "licences.json")
|
|
if not os.path.isfile(path):
|
|
findings.append(f"{corpus}: no licences.json")
|
|
return
|
|
try:
|
|
with open(path, encoding="utf-8") as fh:
|
|
doc = json.load(fh)
|
|
except (OSError, ValueError) as e:
|
|
findings.append(f"{corpus}/licences.json: does not parse: {e}")
|
|
return
|
|
if not isinstance(doc, dict) or doc.get("format") != FORMAT or not isinstance(doc.get("vectors"), dict):
|
|
findings.append(f"{corpus}/licences.json: not a {FORMAT} document with a `vectors` object")
|
|
return
|
|
records = doc["vectors"]
|
|
vdir = os.path.join(base, "vectors")
|
|
dirs = sorted(d for d in os.listdir(vdir) if os.path.isdir(os.path.join(vdir, d))) if os.path.isdir(vdir) else []
|
|
try:
|
|
hashes = names_in(os.path.join(base, "expected_hashes.json")) or {}
|
|
sha = names_in(os.path.join(base, "sha256.json")) or {}
|
|
except ValueError as e:
|
|
findings.append(f"{corpus}: a hash manifest does not parse: {e}")
|
|
return
|
|
for name in sorted(set(dirs) | set(hashes) | set(sha)):
|
|
if name not in records:
|
|
findings.append(f"{corpus}: vector {name} has no licence record")
|
|
for name in sorted(records):
|
|
where = f"{corpus}/licences.json: {name}"
|
|
rec = records[name]
|
|
if name not in dirs:
|
|
findings.append(f"{where}: no directory vectors/{name}")
|
|
if not isinstance(rec, dict):
|
|
findings.append(f"{where}: not an object")
|
|
continue
|
|
for k in sorted(set(rec) - VECTOR_KEYS):
|
|
findings.append(f"{where}: unknown member {k!r}")
|
|
lic = rec.get("licence")
|
|
if lic not in LICENCES:
|
|
findings.append(f"{where}: licence {lic!r} is not one of {', '.join(sorted(LICENCES))}")
|
|
check_content(root, where, rec, findings)
|
|
trained = isinstance(hashes.get(name), dict) and hashes[name].get("trained") is True
|
|
tr = rec.get("training")
|
|
if trained and tr is None:
|
|
findings.append(f"{where}: a trained model with no training record")
|
|
elif not trained and tr is not None:
|
|
findings.append(f"{where}: a training record on a vector whose model is not marked trained")
|
|
if isinstance(tr, dict):
|
|
tw = where + " training"
|
|
for k in sorted(set(tr) - TRAINING_KEYS):
|
|
findings.append(f"{tw}: unknown member {k!r}")
|
|
check_content(root, tw, tr, findings)
|
|
if (tr.get("content") == "third-party" or "licence" in tr) and tr.get("licence") not in LICENCES:
|
|
findings.append(f"{tw}: licence {tr.get('licence')!r} is not one of {', '.join(sorted(LICENCES))}")
|
|
elif tr is not None:
|
|
findings.append(f"{where}: training is not an object")
|
|
if lic in LICENCES:
|
|
split.setdefault(corpus, {}).setdefault(lic, []).append(name)
|
|
|
|
|
|
def main(argv):
|
|
root = "."
|
|
args = argv[1:]
|
|
if args[:1] == ["--root"] and len(args) == 2:
|
|
root = args[1]
|
|
elif args:
|
|
print("usage: check-wai-corpus-licences.py [--root DIR]", file=sys.stderr)
|
|
return 2
|
|
findings, split = [], {}
|
|
for corpus in CORPORA:
|
|
check_corpus(root, corpus, findings, split)
|
|
for f in findings:
|
|
print(f)
|
|
if findings:
|
|
print(f"check-wai-corpus-licences: {len(findings)} finding(s)")
|
|
return 1
|
|
for corpus in CORPORA:
|
|
for lic, names in sorted(split.get(corpus, {}).items()):
|
|
print(f"{corpus}: {lic}: {', '.join(names)}")
|
|
print("check-wai-corpus-licences: every vector has a licence record")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv))
|