#!/usr/bin/env bash # # check-wai-ops-doors-selftest.sh — check-wai-ops-doors.py can fail. # # A check that cannot fail is decoration. This builds a scratch tree shaped like the # repository (a catalogue export, an MCP tool table, an OpenAPI document, the C header and a # Swift and a Kotlin source) whose every door names its operations as the catalogue does, and # asserts that the check passes it and fails each way a door can misname an operation, # saying which. set -uo pipefail here=$(cd "$(dirname "$0")" && pwd) check="$here/check-wai-ops-doors.py" tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT mk() { rm -rf "$tmp/wai" "$tmp/joule-code" mkdir -p "$tmp/wai/catalogue" "$tmp/wai/wai-rs/src/bin/wai_cli/mcp" "$tmp/wai/wai-rs/include" \ "$tmp/joule-code/joule-code-rs/crates/svc" "$tmp/wai/wai-sink-swift/Sources/WaiSink" \ "$tmp/wai/wai-sink-android/core/src/main/kotlin/wai/sink" cat > "$tmp/wai/catalogue/ops.json" <<'J' {"format": "wai.ops/1", "operations": [ {"id": "envelope.inspect", "placement": "local", "route": null}, {"id": "receipt.verify", "placement": "local", "route": null}, {"id": "room.grant", "placement": "service", "route": {"method": "POST", "path": "/v1/rooms/{room}/grants"}} ]} J cat > "$tmp/wai/wai-rs/src/bin/wai_cli/mcp/tools.rs" <<'R' pub(crate) const TOOLS: &[Tool] = &[ Tool { name: "envelope_inspect", title: "Inspect", run: inspect }, Tool { name: "room_grant", title: "Grant", run: grant, }, ]; R cat > "$tmp/joule-code/joule-code-rs/crates/svc/openapi.json" <<'J' {"openapi": "3.1.0", "paths": {"/v1/rooms/{room}/grants": {"post": {"operationId": "room.grant"}, "parameters": []}}} J cat > "$tmp/wai/wai-rs/include/wai.h" <<'H' int32_t wai_envelope_inspect(const uint8_t *p, size_t n); int32_t wai_envelope_pack(const uint8_t *p, size_t n); void wai_buffer_free(struct WaiBuffer b); int32_t wai_image_png_decode(const uint8_t *p); H cat > "$tmp/wai/wai-sink-swift/Sources/WaiSink/Envelope.swift" <<'S' public final class WaiEnvelope: @unchecked Sendable { public init() {} public func inspect(_ path: String) -> String { "{" } public func pack() {} private func verifyQuietly() {} } public final class WaiSink { public func info() -> String { "" } } S cat > "$tmp/wai/wai-sink-android/core/src/main/kotlin/wai/sink/WaiReceipt.kt" <<'K' package wai.sink class WaiReceipt( val owner: String, private val pool: Runnable = Runnable { }, ) { fun verify(bytes: ByteArray): Boolean = bytes.isNotEmpty() private fun checkQuietly() {} companion object { fun verifyAll(list: List) = list.all { it.isNotEmpty() } } } K } fails=0 expect() { # expect [text the output must hold] local want=$1 what=$2 needle=${3:-} out rc out=$(python3 -I "$check" --root "$tmp" 2>&1); rc=$? if [ "$want" = pass ] && [ $rc -ne 0 ]; then echo "FAIL $what: the check failed"; printf '%s\n' "$out" | sed 's/^/ /'; fails=$((fails+1)); return; fi if [ "$want" = fail ] && [ $rc -eq 0 ]; then echo "FAIL $what: the check passed"; printf '%s\n' "$out" | sed 's/^/ /'; fails=$((fails+1)); return; fi if [ -n "$needle" ] && ! grep -qF -- "$needle" <<<"$out"; then echo "FAIL $what: the output does not say '$needle'"; printf '%s\n' "$out" | sed 's/^/ /'; fails=$((fails+1)); return; fi echo "ok $what" } sub() { # sub : one replacement, which must apply python3 - "$1" "$2" "$3" <<'P' || { echo "FAIL the fixture edit did not apply: $2"; fails=$((fails+1)); } import sys p, a, b = sys.argv[1:] s = open(p).read() if s.count(a) != 1: sys.exit(1) open(p, "w").write(s.replace(a, b)) P } mk; expect pass "a tree whose every door names its operations as the catalogue does" "MCP: 2 tools, each an operation" mk; expect pass "the C door's held names and its library API" "C: 1 functions name operations of the catalogue" mk; expect pass "a Swift and a Kotlin method that name operations" "Kotlin: 2 methods name operations" mk; rm "$tmp/wai/wai-rs/src/bin/wai_cli/mcp/tools.rs" expect pass "a door with no operation yet says so" "no MCP door yet" mk; sub "$tmp/wai/wai-rs/src/bin/wai_cli/mcp/tools.rs" '"envelope_inspect"' '"inspect_envelope"' expect fail "an MCP tool misnamed" "tool 'inspect_envelope' names inspect.envelope, which the catalogue does not hold" mk; sub "$tmp/wai/wai-rs/src/bin/wai_cli/mcp/tools.rs" '"room_grant"' '"dev_room_grant"' expect fail "an MCP tool that is not noun_verb" "tool 'dev_room_grant' is not noun_verb" mk; cat > "$tmp/wai/wai-rs/src/bin/wai_cli/mcp/tools.rs" <<'R' pub(crate) const TOOLS: &[Tool] = &[ Tool { op: "envelope.inspect", toolset: Toolset::Core, effect: Effect::Reads, run: inspect, description: "What an envelope holds." }, Tool { op: "room.grant", toolset: Toolset::Dev, effect: Effect::DevRoom, run: grant, description: "A grant." }, ]; R expect pass "a tool table that names each tool by its operation" "MCP: 2 tools, each an operation" mk; cat > "$tmp/wai/wai-rs/src/bin/wai_cli/mcp/tools.rs" <<'R' pub(crate) const TOOLS: &[Tool] = &[ Tool { op: "envelope.inspect", toolset: Toolset::Core, run: inspect, description: "x" }, Tool { op: "receipt.check", toolset: Toolset::Core, run: check, description: "y" }, ]; R expect fail "a tool named by an operation the catalogue does not hold" "tool 'receipt_check' names receipt.check" mk; printf 'pub(crate) const TOOLS: &[Tool] = &[];\n' > "$tmp/wai/wai-rs/src/bin/wai_cli/mcp/tools.rs" expect fail "an MCP tool table the check cannot read" "no tool name could be read" mk; sub "$tmp/joule-code/joule-code-rs/crates/svc/openapi.json" '"room.grant"' '"grant"' expect fail "an OpenAPI operationId misnamed" "operationId 'grant' is not an operation of the catalogue" mk; sub "$tmp/joule-code/joule-code-rs/crates/svc/openapi.json" '/v1/rooms/{room}/grants' '/grant' expect fail "an OpenAPI route that is not the catalogue's" "the catalogue routes room.grant to POST /v1/rooms/{room}/grants" mk; sub "$tmp/joule-code/joule-code-rs/crates/svc/openapi.json" '"room.grant"' '"envelope.inspect"' expect fail "an OpenAPI operation the catalogue places on this machine" "envelope.inspect is placed local, not on a service" mk; printf 'int32_t wai_envelope_decode(const uint8_t *p);\n' >> "$tmp/wai/wai-rs/include/wai.h" expect fail "a C function misnamed" "wai_envelope_decode names envelope.decode, which the catalogue does not hold" mk; printf 'int32_t wai_receipt_get(const uint8_t *p);\n' >> "$tmp/wai/wai-rs/include/wai.h" expect fail "a C function with a near-synonym verb" "wai_receipt_get: the verb get is a near-synonym; the operation verb is show" mk; printf 'int32_t wai_room_up(const uint8_t *p);\n' >> "$tmp/wai/wai-rs/include/wai.h" expect fail "a C function whose verb is up, start's near-synonym" "wai_room_up: the verb up is a near-synonym; the operation verb is start" # upscale and tonemap joined N3's list after its approval: a door's name with either verb is an # operation's name, held like any other. mk; printf 'int32_t wai_envelope_upscale(const uint8_t *p);\n' >> "$tmp/wai/wai-rs/include/wai.h" expect fail "a C function whose verb is upscale" "wai_envelope_upscale names envelope.upscale, which the catalogue does not hold" mk; sub "$tmp/wai/wai-sink-swift/Sources/WaiSink/Envelope.swift" 'public func pack() {}' 'public func tonemap() {}' expect fail "a Swift method whose verb is tonemap" "WaiEnvelope.tonemap names envelope.tonemap, which the catalogue does not hold" mk; sub "$tmp/wai/wai-sink-swift/Sources/WaiSink/Envelope.swift" 'public func pack() {}' 'public func verify() {}' expect fail "a Swift method misnamed" "WaiEnvelope.verify names envelope.verify, which the catalogue does not hold" mk; sub "$tmp/wai/wai-sink-android/core/src/main/kotlin/wai/sink/WaiReceipt.kt" 'fun verify(bytes' 'fun check(bytes' expect fail "a Kotlin method misnamed" "WaiReceipt.check names receipt.check, which the catalogue does not hold" mk; sub "$tmp/wai/wai-sink-android/core/src/main/kotlin/wai/sink/WaiReceipt.kt" 'fun verifyAll(' 'fun describeAll(' expect fail "a Kotlin companion method with a near-synonym verb" "WaiReceipt.describeAll: the verb describe is a near-synonym" mk; sub "$tmp/wai/wai-sink-android/core/src/main/kotlin/wai/sink/WaiReceipt.kt" 'private fun checkQuietly' 'fun checkQuietly' expect fail "a private Kotlin method made public under an operation verb" "WaiReceipt.checkQuietly names receipt.check" if [ "$fails" -ne 0 ]; then echo "check-wai-ops-doors-selftest: $fails case(s) failed" exit 1 fi echo "check-wai-ops-doors-selftest: every case held"