#!/usr/bin/env bash # audit-isv.sh — enforce "every numerical constant is ISV-resident" # per SP20 §0.1. # # Walks scripts/audit-manifest/kernels.txt and greps each listed .cu # for `#define NAME VALUE` lines where VALUE is a numeric literal. # Flags any define whose NAME is NOT in the structural-dim allowlist # AND does NOT end in `_INDEX` (ISV slot pointer). # # Exit codes: # 0 — zero violations # 1 — one or more violations found # 2 — usage / setup error # # Allowlist for structural-dim names (compile-time required for buffer # layouts / atom support / shared memory): ALLOWED_NAMES=( "N_ACTIONS" "Q_N_ATOMS" "HIDDEN_DIM" "REGIME_DIM" "MAX_UNITS" "BLOCK_DIM" "GRID_DIM" "BLOCK_SIZE" "WARP_SIZE" "BLOCK_X" "BLOCK_Y" "BLOCK_Z" "MAX_WINDOW_ENTRIES" "BOOK_LEVELS" ) # Action enum indices (`ACTION_ = `) are structural API # contracts matching crates/ml-alpha/src/rl/common.rs::Action enum # positions. Allowed via name-prefix pattern. ACTION_NAME_PREFIX="ACTION_" set -euo pipefail ROOT=$(git rev-parse --show-toplevel) MANIFEST="$ROOT/scripts/audit-manifest/kernels.txt" CUDA_DIR="$ROOT/crates/ml-alpha/cuda" if [[ ! -f "$MANIFEST" ]]; then echo "ERROR: manifest not found: $MANIFEST" >&2 exit 2 fi # Read manifest into array, skipping blank lines and comments. mapfile -t kernels < <(grep -vE '^\s*(#|$)' "$MANIFEST" | awk '{print $1}') if [[ ${#kernels[@]} -eq 0 ]]; then echo "audit-isv: manifest empty — no kernels to audit. PASS." exit 0 fi # Build a regex of allowed names + `_INDEX` suffix pattern. allowed_regex="" for name in "${ALLOWED_NAMES[@]}"; do allowed_regex="${allowed_regex}|^${name}$" done allowed_regex="(${allowed_regex#|}|_INDEX$)" violations=0 total_defines=0 echo "audit-isv: auditing ${#kernels[@]} kernel(s) from manifest..." echo for kernel in "${kernels[@]}"; do cu_file="$CUDA_DIR/${kernel}.cu" if [[ ! -f "$cu_file" ]]; then echo "ERROR: kernel listed in manifest but file missing: $cu_file" >&2 violations=$((violations + 1)) continue fi # Extract `#define NAME VALUE` where VALUE is a numeric literal # (integer or float, optional `f` suffix, optional sign). # Skip macros that contain function-like () bodies — those are # parameterized templates, not constants. while IFS= read -r line; do # Strip leading whitespace trimmed="${line#"${line%%[![:space:]]*}"}" # Parse: #define NAME VALUE if [[ "$trimmed" =~ ^\#define[[:space:]]+([A-Z_][A-Z0-9_]*)[[:space:]]+([+-]?[0-9]+(\.[0-9]+)?f?)$ ]]; then name="${BASH_REMATCH[1]}" value="${BASH_REMATCH[2]}" total_defines=$((total_defines + 1)) if [[ "$name" =~ $allowed_regex || "$name" == ${ACTION_NAME_PREFIX}* ]]; then : # allowed (structural dim, ISV index, or ACTION_ enum mirror) else echo " VIOLATION $kernel: #define $name $value (should be ISV slot)" violations=$((violations + 1)) fi fi done < "$cu_file" done echo echo "audit-isv: scanned $total_defines numeric #defines, found $violations violation(s)." if [[ $violations -gt 0 ]]; then echo echo "Per SP20 §0.1: every numerical constant in new kernels must be ISV-resident." echo "Add an RL_*_INDEX slot to crates/ml-alpha/src/rl/isv_slots.rs," echo "seed via rl_isv_write at trainer init, and read from the slot in the kernel." exit 1 fi echo "audit-isv: PASS" exit 0