#!/usr/bin/env bash # Guard the `mc-ai::abstract_state` docs contract. # # The AbstractRolloutState POD is the Rust↔WGSL interface. Every public field's # docstring must include its byte offset (e.g. `@24`) so readers of either side # can cross-reference without opening the other. `deny(missing_docs)` catches # *missing* docs; this script catches docs that drift off the offset contract. # # Run from EDIT host (pure text scan, no toolchain needed): # bash tools/check-abstract-state-docs.sh # # On RUN host, `cargo doc -p mc-ai --no-deps` also enforces the `deny` at build # time — that's the authoritative gate. This script is the fast local check. set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" FILE="$REPO_ROOT/src/simulator/crates/mc-ai/src/abstract_state.rs" if [[ ! -f "$FILE" ]]; then echo "ERROR: $FILE not found" >&2 exit 2 fi # Every public field (including `_padN`) must have `@` somewhere in its # doc comments. We walk the AbstractPlayerState struct body and flag any # `pub : ,` line whose preceding doc block is missing `@\d+`. python3 - "$FILE" <<'PY' import re import sys from pathlib import Path src = Path(sys.argv[1]).read_text() # Isolate the AbstractPlayerState struct body. m = re.search( r"pub struct AbstractPlayerState \{(.*?)\n\}", src, re.DOTALL, ) if not m: print("ERROR: could not locate AbstractPlayerState struct body", file=sys.stderr) sys.exit(2) body = m.group(1) # Walk lines, track the current doc block, check each `pub : ,`. lines = body.splitlines() doc_block: list[str] = [] failures: list[tuple[str, str]] = [] for raw in lines: line = raw.strip() if line.startswith("///"): doc_block.append(line) continue field_m = re.match(r"pub (\w+):", line) if field_m: field = field_m.group(1) doc_text = " ".join(doc_block) if not re.search(r"@\d+", doc_text): failures.append((field, doc_text or "")) doc_block = [] elif line and not line.startswith("//"): # Non-doc, non-field line — reset the doc block. doc_block = [] if failures: print("FAIL: AbstractPlayerState fields missing `@` in docstring:") for field, doc in failures: print(f" - {field}: {doc[:80]}") sys.exit(1) print("OK: every AbstractPlayerState field docstring includes a byte offset.") PY