open-standards/scripts/check-wai-quantum-names.py
Transaction Science 62f4d28d4a wai: every wai.quantum string is registered, listed with what it names, or a schema's name
The registry's unregistered labels now hold every wai.quantum string the tree uses
that no revision registers, each saying what it names, grouped: the receipt kinds,
the modules of wai-quantum, the names a record's members carry, the fabric's kernels
and corpora, the circuit contracts' revisions, the documents with no schema file, the
digest domains, and the strings the tests and the provider corpus give a reader to see
it refuse them. A document's name, wai.quantum.<document>/<major>, is accounted for by
its schema file (schemas/<document>.v<major>.json, its title the name) and needs no row.

scripts/check-wai-quantum-names.py reads the registry export and the schema files and
every text file but the changelog, and names the file and line of any string neither
accounts for; it also fails on a pattern that matches nothing, a schema file whose
title is not its file's name, a quantum label nothing outside the registry uses, and a
string held twice. The repository checks run it and its self-test. The wai-quantum
constants test compares the receipt kinds alone, and the registry test refuses a label
listed twice.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-06 17:05:39 -05:00

216 lines
9 KiB
Python
Executable file

#!/usr/bin/env python3
"""check-wai-quantum-names.py: every `wai.quantum.*` string in the tree is accounted for.
A string of the quantum namespace is one of:
- a string the registry registers (a capability or a record), or one of its named
identifiers, unregistered labels or wire identifiers. The registry is one table,
wai/wai-rs/src/codecs/registry.rs, exported by `wai registry export` as
wai/registry/capabilities.json (CI holds the export to the table with
`wai registry export --check`). This reads the export. A label there says what it
names: a module of the quantum crates, a receipt kind, a kernel, a document with no
schema file, or a string a test gives a reader to see it refused.
- the name of a document a quantum crate writes, `wai.quantum.<document>/<major>`,
kept as a JSON Schema at `schemas/<document>.v<major>.json` beside the crate's
manifest, whose `title` is the name (wai/wai-quantum/VERSIONING.md, *The schemas*).
A name such a file gives as the `const` of a member (a document carried inside
another) is accounted for the same way. The schema files are the list: a new
document adds its file, and no line anywhere else.
A string is `wai.quantum.` followed by dot-separated segments of letters, digits, `_`
and `-`, and optionally `/` and a revision (`wai.quantum.circuit2`,
`wai.quantum.cal.noise_learn`, `wai.quantum.job-result/1`, `wai.quantum.sv/run-wide`).
A pattern `wai.quantum.<segments>.*` is accounted for when some accounted string starts
with `wai.quantum.<segments>.`. A placeholder (`wai.quantum.<document>`) or a prefix
(`"wai.quantum."`) is not a string and is not read.
Every text file git tracks or would track is read, except the changelog
(wai/CHANGELOG.md and the entry files of wai/changes/): it records names as they were
when each change was made. Binary files are not read.
Findings, one line each:
- a string accounted for by none of the above, with its file and line;
- a quantum schema file whose `title` is not the name its file name gives;
- a `wai.quantum.*` label or wire identifier that nothing outside the registry's own
files (registry.rs, its export, REGISTRY.md) uses, so the list stays the set of
strings in use;
- a `wai.quantum.*` string the registry holds twice.
check-wai-quantum-names.py [--root DIR]
Exit 0 when every string is accounted for, 1 with one line a finding otherwise, 2 when
the tree cannot be read. Standard library only.
"""
import json
import os
import re
import subprocess
import sys
PREFIX = "wai.quantum."
# A string of the namespace, with its revision and a trailing `.*` (a pattern).
TOKEN = re.compile(
r"wai\.quantum\.([A-Za-z0-9_][A-Za-z0-9_-]*(?:\.[A-Za-z0-9_][A-Za-z0-9_-]*)*)"
r"(/[A-Za-z0-9_-]+)?(\.\*)?"
)
EXPORT = "wai/registry/capabilities.json"
# The registry's own files: the table, its export and the page that prints it.
REGISTRY_FILES = {"wai/wai-rs/src/codecs/registry.rs", EXPORT, "wai/REGISTRY.md"}
# The record of past changes, read by nothing here.
HISTORY = ("wai/CHANGELOG.md", "wai/changes/")
SCHEMA_FILE = re.compile(r"(?:^|/)schemas/([a-z0-9_-]+(?:\.[a-z0-9_-]+)*)\.v([0-9]+)\.json$")
def fail(msg):
print(f"check-wai-quantum-names: {msg}", file=sys.stderr)
sys.exit(2)
def git(root, *args):
r = subprocess.run(["git", "-C", root, *args], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
return r.returncode, r.stdout.decode("utf-8", "replace"), r.stderr.decode("utf-8", "replace")
def occurrences(root):
"""Every string of the namespace in the text files git tracks or would track:
[(path, line, string, is_pattern)]."""
rc, out, err = git(root, "grep", "-I", "-n", "-z", "--untracked", "-F", "-e", PREFIX)
if rc == 1:
return []
if rc != 0:
fail(f"git grep failed ({rc}): {err.strip()}")
found = []
for rec in out.split("\n"):
if not rec:
continue
parts = rec.split("\0", 2)
if len(parts) != 3:
fail(f"unreadable git grep line: {rec[:120]!r}")
path, line, text = parts
if path == HISTORY[0] or path.startswith(HISTORY[1]):
continue
for m in TOKEN.finditer(text):
name = PREFIX + m.group(1) + (m.group(2) or "")
found.append((path, int(line), name, bool(m.group(3))))
return found
def registry(root):
"""The strings the export holds, by table, and the quantum strings it holds twice."""
path = os.path.join(root, EXPORT)
try:
with open(path, encoding="utf-8") as fh:
doc = json.load(fh)
except (OSError, ValueError) as e:
fail(f"cannot read {EXPORT}: {e}")
tables = {
"registered": [c["capability"] for c in doc.get("capabilities", [])],
"identifier": [i["identifier"] for i in doc.get("identifiers", [])],
"label": [l["label"] for l in doc.get("unregistered_labels", [])],
"wire identifier": [w["name"] for w in doc.get("wire_identifiers", [])],
}
if not tables["registered"]:
fail(f"{EXPORT} registers nothing")
held = {}
twice = []
for table, names in tables.items():
for n in names:
if n.startswith(PREFIX):
if n in held:
twice.append(f"{EXPORT}: {n} is held twice ({held[n]}, {table})")
held.setdefault(n, table)
return tables, held, twice
def schema_names(root):
"""The document names the quantum schema files give, and the findings about them."""
rc, out, err = git(root, "ls-files", "-z", "--cached", "--others", "--exclude-standard", "--", "wai/")
if rc != 0:
fail(f"git ls-files failed ({rc}): {err.strip()}")
names, findings = {}, []
def consts(v, acc):
if isinstance(v, dict):
for k, x in v.items():
if k == "const" and isinstance(x, str):
acc.append(x)
else:
consts(x, acc)
elif isinstance(v, list):
for x in v:
consts(x, acc)
for path in sorted(p for p in out.split("\0") if p):
m = SCHEMA_FILE.search(path)
if not m or not os.path.isfile(os.path.join(root, path)):
continue
try:
with open(os.path.join(root, path), encoding="utf-8") as fh:
doc = json.load(fh)
except (OSError, ValueError) as e:
findings.append(f"{path}: not a JSON document ({e})")
continue
title = doc.get("title") if isinstance(doc, dict) else None
if not (isinstance(title, str) and title.startswith(PREFIX)):
continue
want = f"{PREFIX}{m.group(1)}/{m.group(2)}"
if title != want:
findings.append(f"{path}: its title is {title}, but a schema of that file is named {want}")
continue
names.setdefault(title, path)
acc = []
consts(doc, acc)
for c in acc:
t = TOKEN.fullmatch(c)
if t and not t.group(3):
names.setdefault(c, path)
return names, findings
def main(argv):
root = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
if len(argv) == 2 and argv[0] == "--root":
root = os.path.abspath(argv[1])
elif argv:
fail("usage: check-wai-quantum-names.py [--root DIR]")
tables, held, findings = registry(root)
accounted = {n for names in tables.values() for n in names}
schemas, schema_findings = schema_names(root)
findings += schema_findings
accounted |= set(schemas)
used_outside = set()
seen = 0
for path, line, name, pattern in occurrences(root):
seen += 1
if path not in REGISTRY_FILES:
used_outside.add(name)
if pattern:
stem = name + "."
if not any(a.startswith(stem) for a in accounted):
findings.append(f"{path}:{line}: {name}.* matches no registered string, label or schema's name")
continue
if name not in accounted:
findings.append(f"{path}:{line}: {name} is not registered, listed as a label or wire identifier, "
f"or the name of a quantum schema file")
for n, table in sorted(held.items()):
if table in ("label", "wire identifier") and n not in used_outside:
findings.append(f"{EXPORT}: the {table} {n} is used nowhere outside the registry's own files")
if findings:
for f in findings:
print(f)
sys.stdout.flush()
print(f"\n{len(findings)} finding(s). A new `wai.quantum.*` string is registered, or listed with what it "
"names in UNREGISTERED_LABELS (wai/wai-rs/src/codecs/registry.rs, beside its group), then "
"`wai registry export` is run; a document's name is its schema file "
"(`schemas/<document>.v<major>.json`, its `title` the name).", file=sys.stderr)
return 1
print(f"check-wai-quantum-names: every wai.quantum string is accounted for "
f"({seen} occurrences read; {len(schemas)} named by schema files)")
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))