#!/usr/bin/env bash # tooling/godot-cold-parse [--run-menu] — cold-cache headless parse check. # # Used by /pr-process step 1c before push. Deletes the cached script-class # registry so the parse simulates the cold-start ordering CI / fresh clones # see: Sprint 36 close caught a new `class_name MetaScreen` base class and # six extending scripts that parsed fine on warm developer caches but hit # `Could not find base class "MetaScreen"` post-merge, because the # autoload-vs-class_name registration order only resolves correctly once the # class cache is seeded (see CLAUDE.md -> GDScript conventions -> Autoload # parse-order rule). # # Godot's resource scanner emits category errors (e.g. "Export type can only # be built-in, a resource, a node, or an enum") that do NOT always prefix # with SCRIPT ERROR — they appear as plain ERROR lines. The filter below # catches both, then drops known pre-existing noise from the autoload # class_name parse-order trap. Sprint 36 shipped a scanner error the old # narrower grep missed; this is why the filter stays wide. # # --run-menu: also launch main_menu.tscn briefly (for branches with UI changes). # # Exit 0 + "clean" if no matches. Exit 1 + the matched lines if any are found. set -euo pipefail REPO_ROOT="$(git rev-parse --show-toplevel)" RUN_MENU=false [ "${1:-}" = "--run-menu" ] && RUN_MENU=true rm -f "$REPO_ROOT/client/.godot/global_script_class_cache.cfg" FILTER='^(SCRIPT )?ERROR|Parse Error|Export type' MATCHES=$(godot --headless --path "$REPO_ROOT/client" --quit 2>&1 \ | grep -iE "$FILTER" \ | grep -v "Failed loading resource: res://assets" \ | grep -v "Cannot infer the type" \ | grep -vE '(Messagepack|LocalBridge|ServerProcess|Constants)" not declared' || true) if [ "$RUN_MENU" = true ]; then MENU_MATCHES=$(timeout 10 godot --path "$REPO_ROOT/client" res://scenes/main_menu.tscn 2>&1 \ | grep -iE "$FILTER" || true) if [ -n "$MENU_MATCHES" ]; then MATCHES="$MATCHES $MENU_MATCHES" fi fi if [ -n "$MATCHES" ]; then echo "$MATCHES" exit 1 fi echo "godot-cold-parse: clean"