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 8s
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 8s
wai-audit collector and player integration / does this change touch it (pull_request) Successful in 7s
WAI operation catalogue / does this change touch it (pull_request) Successful in 7s
wai-app and Create / wai-app · does this change touch it (pull_request) Successful in 15s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (pull_request) Successful in 14s
WAI conformance / does this change touch it (pull_request) Successful in 5s
WAI examples / WAI examples · does this change touch them (pull_request) Successful in 7s
wai-fleet service / does this change touch it (pull_request) Successful in 5s
WAI energy receipts / does this change touch it (pull_request) Successful in 18s
WAI JPEG-AI whole decode (pinned inputs, required) / does this change touch it (pull_request) Successful in 8s
wai-key-proxy / does this change touch it (pull_request) Successful in 8s
wai-ledger service / does this change touch it (pull_request) Successful in 5s
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 8s
wai-quantum docs, course and locales / does this change touch it (pull_request) Successful in 6s
wai-ops-gpu integer operators on GPUs / does this change touch it (pull_request) Successful in 9s
WAI neural sink gates / does this change touch it (pull_request) Successful in 19s
wai-quantum for JavaScript / does this change touch it (pull_request) Successful in 7s
wai-quantum WebGPU member / does this change touch it (pull_request) Successful in 6s
wai-quantum studio and desktop / does this change touch it (pull_request) Successful in 10s
wai-sink-android native sink library / does this change touch it (pull_request) Successful in 7s
WAI mbt2018_mean integer decode (regenerated vectors) / does this change touch it (pull_request) Successful in 11s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 7s
wai-web wasm32 / does this change touch it (pull_request) Successful in 8s
WAI Studio / does this change touch it (pull_request) Successful in 11s
wai-sink-swift native sink package / does this change touch it (pull_request) Successful in 17s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 3m4s
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 3m16s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 2m5s
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Successful in 5m8s
WAI conformance / fast integer kernels, every x86_64 kernel set (AVX-512 silicon) (pull_request) Successful in 3m22s
joule-code conformance gate / joule-moq-relay, its own energy on a counter (macos) (pull_request) Successful in 36s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (pull_request) Successful in 35s
wai-audit collector and player integration / collector · linux (pull_request) Successful in 1m8s
wai-quantum WebGPU member / member · DX12 and Vulkan (cbio-windows) (pull_request) Successful in 12m1s
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 2m42s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 27s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (pull_request) Successful in 3m10s
WAI conformance / faces from keypoints under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m42s
WAI operation catalogue / the catalogue and every door · linux (pull_request) Successful in 6m13s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 4m14s
wai-quantum WebGPU member / member · integrated GPU, Vulkan (framework-128gb) (pull_request) Successful in 10m34s
wai-fleet service / service · linux (pull_request) Successful in 2m26s
wai-ledger service / service · linux (pull_request) Successful in 2m2s
wai-key-proxy / proxy · linux (pull_request) Successful in 2m59s
wai-pack / wai-pack · linux (pull_request) Successful in 2m5s
wai-quantum docs, course and locales / the Studio's words in every locale · linux (pull_request) Successful in 26s
wai-quantum docs, course and locales / the course and the command line's languages · linux (pull_request) Successful in 2m45s
wai-quantum WebGPU member / member · GPU, Vulkan (jetson-hub) (pull_request) Successful in 22m31s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 7m26s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 2m2s
WAI examples / WAI examples · portable · linux (pull_request) Successful in 9m31s
WAI neural sink gates / the exact JPEG-AI decoder reports its path (pinned inputs, required) (pull_request) Successful in 2m52s
wai-quantum WebGPU member / member · GPU, Vulkan (thor) (pull_request) Successful in 24m59s
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Successful in 2m16s
wai-quantum for JavaScript / javascript · linux (pull_request) Successful in 3m47s
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness (pull_request) Successful in 3m57s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 3m53s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 2m57s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (pull_request) Successful in 1m6s
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Successful in 8m6s
wai-quantum studio and desktop / desktop app · windows (pull_request) Successful in 9m36s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 14m34s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 6m37s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 5m30s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 19m54s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 5m27s
wai-quantum studio and desktop / desktop app · linux (pull_request) Successful in 12m3s
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 10m30s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (pull_request) Successful in 22m30s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (pull_request) Successful in 1m2s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (pull_request) Successful in 1m52s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 12m16s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (pull_request) Successful in 1m20s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 17m11s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 30s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 1m10s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 3m54s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (pull_request) Successful in 7m52s
wai-fleet service / service · macos (pull_request) Successful in 43s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 30s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 6m52s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 9m7s
wai-ledger service / service · macos (pull_request) Successful in 1m26s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 17s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 20m26s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 43s
wai-pack / wai-pack · macos (pull_request) Successful in 4m19s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 2m38s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 13m34s
wai-quantum WebGPU member / member · Metal (macos) (pull_request) Successful in 3m26s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 19m27s
wai-quantum WebGPU member / member · browser WebGPU (macos) (pull_request) Successful in 2m5s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (pull_request) Successful in 23m32s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 2m27s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 7m32s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 37s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 19s
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Successful in 11m58s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 5m11s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Successful in 7m35s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 3m25s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 7m20s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 12m0s
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Successful in 10m54s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Successful in 17m29s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 1h30m12s
The change of wai/toolchain-fixes (69f88e62f..074658780, 21 commits), on the fix branch: its changelog entries as files of wai/changes/, its wai-conformance.yml edits as step files, corpus files and test tags, and every other file as the branch has it. - b04013914 holdings: the registry lists WHD1, the record's magic (§4.1) - a184abb87 catalogue: upscale and tonemap are recorded in N3's verb list, and the doors' check knows them - 8db31a778 wai mcp: MCP 2026-07-28, the stateless revision, beside the three initialize negotiates - 052830194 wai.json: $schema, wai and name, and the schema of version 1 committed beside the reader - 163fc2017 wai create: every template carries an AGENTS.md - fe9b26fbc joule-moq-room: the example names the relay's WebSocket port, 4480, and one test holds every port - 359b5ba9d quickstart: the archive carries WAI Studio, wai-ingest and wai-quantum - 6334d88c8 joule-moq-room: the port table holds every daemon's default, and the listeners off until set - d38610de6 wai.json: the two workflows that hold the schema to its reader run when the schema changes - 179df52b7 catalogue: the two verbs joined the list after wai 0.4.0, not in toolchain 0.3.0 - 971cf5862 wai.json: a file of a later version is refused as that version before any member it adds - 1a59a39f7 wai.json: the reader takes a whole number however it is written, and the tests run the name pattern - d752fd9b4 wai.json: the release waits for the schema to be served, and version 1 is fixed by the first release that writes it - c213ed9db wai mcp: -32022 names as supported only the revisions a request may name, never the one asked for - 99b800f29 wai mcp: initialize negotiates 2025-11-25, the last revision with a handshake - e33021c3f catalogue: the near-synonym list is held in every text that states it, and the doors' check knows up - b99bb70be catalogue: the changelog records the near-synonym list held with the verb list - a0beb2163 quickstart: the wai formula and manifest depend on wai-quantum's own instead of installing a second copy - 0e5084031 quickstart: Studio's build leaves the checkout as it found it, and the examples workflow runs clean-machine.sh - 4cc4ae3d7 joule-moq-room: the port test reads the gateway's example with the gateway's own reader, and starts the gateway from it - 074658780 quickstart: the changelog names where Studio's build goes, not where it went before it shipped Co-Authored-By: Claude Opus 5.5 <[email protected]>
296 lines
12 KiB
Python
Executable file
296 lines
12 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""check-wai-ops-doors.py: every door of WAI names its operations as the catalogue does.
|
|
|
|
WAI's operations are one catalogue, `noun.verb` (wai/wai-rs/src/catalogue.rs), exported as
|
|
wai/catalogue/ops.json by `wai ops export` (CI holds the export to the table with
|
|
`wai ops export --check`). Each door names an operation by a mechanical transform of its id:
|
|
|
|
MCP tool `noun_verb` (or its `op`) wai/wai-rs/src/bin/wai_cli/mcp/tools.rs
|
|
HTTP API operationId `noun.verb` every openapi.json of the services
|
|
C function `wai_noun_verb` wai/wai-rs/include/wai.h
|
|
Swift type `Wai<Noun>`, method `<verb>…` wai/wai-sink-swift/Sources
|
|
Kotlin class `Wai<Noun>`, fun `<verb>…` wai/wai-sink-android/*/src/main
|
|
|
|
The command line is held by the `wai` binary's own tests (wai_cli/door.rs).
|
|
|
|
What each door is held to:
|
|
|
|
- MCP: every tool's name is `noun_verb` of an operation in the catalogue.
|
|
- HTTP API: every operationId is an operation of the catalogue that runs on a service
|
|
(placement `service` or `both`), at the route the catalogue gives it.
|
|
- C, Swift, Kotlin: a name in a catalogue noun's namespace whose verb is an operation verb
|
|
(the closed list below) names an operation, and that operation must be in the catalogue;
|
|
a near-synonym verb (`get`, `describe`, `info`, `keygen`, `init`, `new`, `up`) in a catalogue
|
|
noun's namespace is refused. Any other name is library API the catalogue does not list
|
|
(the C ABI's codec and sink functions, the native sinks' classes), and is reported as such.
|
|
|
|
A door that has no operation of the catalogue yet is reported as such; that is not a
|
|
failure. A wrong name is.
|
|
|
|
check-wai-ops-doors.py [--root DIR] [--ops FILE]
|
|
|
|
Exit 0 when every door holds, 1 with one line a finding otherwise. Standard library only.
|
|
"""
|
|
import glob
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
# The operation verbs: the closed list of WAI toolchain v1 (naming rule N3), with the two it
|
|
# gained after its approval, upscale and tonemap (wai/catalogue/README.md). The catalogue's
|
|
# own copy is wai/wai-rs/src/catalogue.rs's VERBS; a test there holds this one to it.
|
|
VERBS = {
|
|
"create", "list", "show", "update", "delete", "check", "verify", "run", "build", "export",
|
|
"import", "start", "stop", "join", "leave", "grant", "invite", "revoke", "rotate", "tail",
|
|
"search", "test", "load", "encode", "decode", "inspect", "wrap", "unwrap", "fetch",
|
|
"generate", "promote", "rollback", "lock", "upgrade", "setup", "guide", "stream",
|
|
"upscale", "tonemap",
|
|
}
|
|
# Near-synonyms of an operation verb, and the verb to use: the catalogue's NEAR_SYNONYMS
|
|
# (wai/wai-rs/src/catalogue.rs), which a test there holds this one to.
|
|
NEAR = {"get": "show", "describe": "show", "info": "show", "keygen": "create", "init": "create", "new": "create", "up": "start"}
|
|
|
|
MCP_TABLE = "wai/wai-rs/src/bin/wai_cli/mcp/tools.rs"
|
|
OPENAPI_GLOBS = ["joule-code/joule-code-rs/crates/*/openapi.json", "wai/*/openapi.json", "wai/wai-rs/openapi.json"]
|
|
C_HEADER = "wai/wai-rs/include/wai.h"
|
|
SWIFT_GLOB = "wai/wai-sink-swift/Sources/**/*.swift"
|
|
KOTLIN_GLOB = "wai/wai-sink-android/*/src/main/**/*.kt"
|
|
|
|
|
|
def load_ops(path):
|
|
with open(path, encoding="utf-8") as f:
|
|
doc = json.load(f)
|
|
if doc.get("format") != "wai.ops/1":
|
|
raise SystemExit(f"{path}: not a catalogue export (format {doc.get('format')!r})")
|
|
return {o["id"]: o for o in doc["operations"]}
|
|
|
|
|
|
def split_id(i):
|
|
n, _, v = i.partition(".")
|
|
return n, v
|
|
|
|
|
|
def check_mcp(root, ops, findings, notes):
|
|
path = os.path.join(root, MCP_TABLE)
|
|
if not os.path.exists(path):
|
|
notes.append("MCP: no MCP door yet (no tool table); nothing to hold")
|
|
return
|
|
with open(path, encoding="utf-8") as f:
|
|
src = f.read()
|
|
# A tool is named outright (`name: "noun_verb"`) or by its operation (`op: "noun.verb"`),
|
|
# its name then the operation's with `_` for the dot.
|
|
names = []
|
|
for body in re.findall(r"\bTool\s*\{(.*?)\}\s*,", src, re.S):
|
|
named = re.search(r'\bname:\s*"([^"]+)"', body)
|
|
by_op = re.search(r'\bop:\s*"([^"]+)"', body)
|
|
if named:
|
|
names.append(named.group(1))
|
|
elif by_op:
|
|
names.append(by_op.group(1).replace(".", "_", 1))
|
|
if not names:
|
|
findings.append(f"MCP: {MCP_TABLE}: no tool name could be read (`Tool {{ name: \"…\" }}` or `Tool {{ op: \"…\" }}`)")
|
|
return
|
|
bad = 0
|
|
for t in names:
|
|
if t.count("_") != 1:
|
|
findings.append(f"MCP: tool {t!r} is not noun_verb of an operation")
|
|
bad += 1
|
|
continue
|
|
i = t.replace("_", ".")
|
|
if i not in ops:
|
|
findings.append(f"MCP: tool {t!r} names {i}, which the catalogue does not hold")
|
|
bad += 1
|
|
if not bad:
|
|
notes.append(f"MCP: {len(names)} tools, each an operation of the catalogue")
|
|
|
|
|
|
def openapi_operations(doc):
|
|
for path, item in (doc.get("paths") or {}).items():
|
|
for method, op in item.items():
|
|
if method.lower() in ("get", "put", "post", "delete", "patch", "head", "options", "trace") and isinstance(op, dict):
|
|
yield method.upper(), path, op.get("operationId")
|
|
|
|
|
|
def check_openapi(root, ops, findings, notes):
|
|
files = sorted({f for g in OPENAPI_GLOBS for f in glob.glob(os.path.join(root, g))})
|
|
if not files:
|
|
notes.append("HTTP API: no OpenAPI document yet; nothing to hold")
|
|
return
|
|
for f in files:
|
|
rel = os.path.relpath(f, root)
|
|
with open(f, encoding="utf-8") as fh:
|
|
doc = json.load(fh)
|
|
n = bad = 0
|
|
for method, path, oid in openapi_operations(doc):
|
|
n += 1
|
|
where = f"HTTP API: {rel}: {method} {path}"
|
|
if not oid:
|
|
findings.append(f"{where}: no operationId")
|
|
bad += 1
|
|
elif oid not in ops:
|
|
findings.append(f"{where}: operationId {oid!r} is not an operation of the catalogue")
|
|
bad += 1
|
|
else:
|
|
o = ops[oid]
|
|
if o["placement"] not in ("service", "both"):
|
|
findings.append(f"{where}: {oid} is placed {o['placement']}, not on a service")
|
|
bad += 1
|
|
r = o.get("route")
|
|
if r and (r["method"].upper(), r["path"]) != (method, path):
|
|
findings.append(f"{where}: the catalogue routes {oid} to {r['method']} {r['path']}")
|
|
bad += 1
|
|
if not bad:
|
|
notes.append(f"HTTP API: {rel}: {n} operations, each an operation of the catalogue at its route")
|
|
|
|
|
|
def held_name(door, where, noun, verb, nouns, ops, findings):
|
|
"""A name in `noun`'s namespace with `verb`: a finding when it names an operation the
|
|
catalogue does not hold, or uses a near-synonym. True when it names an operation."""
|
|
if noun not in nouns:
|
|
return False
|
|
if verb in NEAR:
|
|
findings.append(f"{door}: {where}: the verb {verb} is a near-synonym; the operation verb is {NEAR[verb]}")
|
|
return True
|
|
if verb not in VERBS:
|
|
return False
|
|
i = f"{noun}.{verb}"
|
|
if i not in ops:
|
|
near = sorted(o for o in ops if split_id(o)[1] == verb)
|
|
hint = f" (operations with that verb: {', '.join(near)})" if near else ""
|
|
findings.append(f"{door}: {where} names {i}, which the catalogue does not hold{hint}")
|
|
return True
|
|
|
|
|
|
def check_c(root, ops, nouns, findings, notes):
|
|
path = os.path.join(root, C_HEADER)
|
|
if not os.path.exists(path):
|
|
notes.append("C: no header; nothing to hold")
|
|
return
|
|
with open(path, encoding="utf-8") as f:
|
|
src = f.read()
|
|
fns = sorted(set(re.findall(r"\b(wai_[a-z0-9_]+)\s*\(", src)))
|
|
mapped = other = 0
|
|
before = len(findings)
|
|
for fn in fns:
|
|
parts = fn.split("_")[1:]
|
|
if len(parts) >= 2 and held_name("C", fn, parts[0], parts[1], nouns, ops, findings):
|
|
mapped += 1
|
|
else:
|
|
other += 1
|
|
if len(findings) == before:
|
|
if mapped:
|
|
notes.append(f"C: {mapped} functions name operations of the catalogue, each held; {other} are library API")
|
|
else:
|
|
notes.append(f"C: no operation of the catalogue in this door yet; its {other} functions are library API (codecs, envelopes, the sink)")
|
|
|
|
|
|
def camel_verb(name):
|
|
m = re.match(r"[a-z]+", name)
|
|
return m.group(0) if m else ""
|
|
|
|
|
|
TYPE_RE = {
|
|
"Swift": re.compile(r"^\s*(?:@\w+\s+)*(public|open)\s+(?:final\s+)?(?:class|struct|enum|actor)\s+(\w+)|^\s*(?:public\s+)?extension\s+(\w+)"),
|
|
"Kotlin": re.compile(r"^\s*(?!private|internal)(?:public\s+)?(?:open\s+|abstract\s+|sealed\s+|data\s+|enum\s+|final\s+)*(?:class|object|interface)\s+(\w+)"),
|
|
}
|
|
FUNC_RE = {
|
|
"Swift": re.compile(r"^\s*(?:@\w+\s+)*(?:public|open)\s+(?:static\s+|class\s+|final\s+|override\s+)*func\s+(\w+)"),
|
|
"Kotlin": re.compile(r"^\s*(?!.*\b(?:private|internal|protected)\b)(?:public\s+|override\s+|suspend\s+|inline\s+|operator\s+)*fun\s+(?:<[^>]*>\s*)?(\w+)"),
|
|
}
|
|
|
|
|
|
def sdk_names(lang, src):
|
|
"""Every (type, method) the source declares public: a method under the type whose body
|
|
holds it, a companion object's under its class. A type's body opens at the first line
|
|
after its declaration that ends with `{` (a Kotlin primary constructor can take several
|
|
lines, and a default argument can hold a lambda's braces)."""
|
|
out = []
|
|
stack = [] # (type name, brace depth inside its body)
|
|
depth = 0
|
|
pending = None
|
|
for line in src.splitlines():
|
|
code = re.sub(r'"(?:\\.|[^"\\])*"', '""', line.split("//")[0])
|
|
name = None
|
|
t = TYPE_RE[lang].match(code)
|
|
if t:
|
|
name = next(g for g in t.groups() if g and g not in ("public", "open"))
|
|
elif lang == "Kotlin" and re.match(r"^\s*companion\s+object\b", code) and stack:
|
|
name = stack[-1][0]
|
|
f = FUNC_RE[lang].match(code)
|
|
if f and stack:
|
|
out.append((stack[-1][0], f.group(1)))
|
|
if name:
|
|
pending = name
|
|
if pending and code.rstrip().endswith("{"):
|
|
stack.append((pending, depth + 1))
|
|
pending = None
|
|
for ch in code:
|
|
if ch == "{":
|
|
depth += 1
|
|
elif ch == "}":
|
|
depth -= 1
|
|
while stack and depth < stack[-1][1]:
|
|
stack.pop()
|
|
return out
|
|
|
|
|
|
def check_sdk(lang, root, pattern, ops, nouns, findings, notes):
|
|
files = sorted(glob.glob(os.path.join(root, pattern), recursive=True))
|
|
if not files:
|
|
notes.append(f"{lang}: no sources; nothing to hold")
|
|
return
|
|
mapped = other = 0
|
|
before = len(findings)
|
|
for f in files:
|
|
with open(f, encoding="utf-8") as fh:
|
|
src = fh.read()
|
|
for typ, meth in sdk_names(lang, src):
|
|
if not typ.startswith("Wai") or len(typ) <= 3:
|
|
other += 1
|
|
continue
|
|
noun = typ[3:].lower()
|
|
where = f"{os.path.relpath(f, root)}: {typ}.{meth}"
|
|
if held_name(lang, where, noun, camel_verb(meth), nouns, ops, findings):
|
|
mapped += 1
|
|
else:
|
|
other += 1
|
|
if len(findings) == before:
|
|
if mapped:
|
|
notes.append(f"{lang}: {mapped} methods name operations of the catalogue, each held; {other} are library API")
|
|
else:
|
|
notes.append(f"{lang}: no operation of the catalogue in this door yet; its {other} public names are the native sink's API, whose nouns the catalogue does not hold")
|
|
|
|
|
|
def main(argv):
|
|
root, ops_path = ".", None
|
|
i = 0
|
|
while i < len(argv):
|
|
if argv[i] == "--root" and i + 1 < len(argv):
|
|
root = argv[i + 1]
|
|
i += 2
|
|
elif argv[i] == "--ops" and i + 1 < len(argv):
|
|
ops_path = argv[i + 1]
|
|
i += 2
|
|
else:
|
|
print(__doc__.strip().splitlines()[0], file=sys.stderr)
|
|
print("usage: check-wai-ops-doors.py [--root DIR] [--ops FILE]", file=sys.stderr)
|
|
return 2
|
|
ops = load_ops(ops_path or os.path.join(root, "wai/catalogue/ops.json"))
|
|
nouns = {split_id(i)[0] for i in ops}
|
|
findings, notes = [], []
|
|
check_mcp(root, ops, findings, notes)
|
|
check_openapi(root, ops, findings, notes)
|
|
check_c(root, ops, nouns, findings, notes)
|
|
check_sdk("Swift", root, SWIFT_GLOB, ops, nouns, findings, notes)
|
|
check_sdk("Kotlin", root, KOTLIN_GLOB, ops, nouns, findings, notes)
|
|
for n in notes:
|
|
print(f"ok {n}")
|
|
for f in findings:
|
|
print(f"FAIL {f}")
|
|
print(f"check-wai-ops-doors: {len(ops)} operations; {len(findings)} finding(s)")
|
|
return 1 if findings else 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv[1:]))
|