#!/usr/bin/env bash set -euo pipefail SCRIPT_DIRECTORY="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" PROJECT_DIRECTORY="$(CDPATH= cd -- "$SCRIPT_DIRECTORY/.." && pwd)" cd "$PROJECT_DIRECTORY" failure_count=0 fail_with_matches() { local label="$1" local matches="$2" if [ -n "$matches" ]; then printf 'ARCHITECTURE ERROR: %s\n%s\n' "$label" "$matches" failure_count=$((failure_count + 1)) fi } source_directories="src tests" if [ -d game ]; then source_directories="$source_directories game" fi duplicate_classes="$( rg --no-filename -o \ '^[[:space:]]*class_name[[:space:]]+[A-Za-z_][A-Za-z0-9_]*' \ $source_directories 2>/dev/null \ | awk '{print $2}' \ | sort \ | uniq -d \ || true )" fail_with_matches "duplicate class_name declarations" "$duplicate_classes" if [ -d game ]; then prototype_imports="$(rg -n 'res://(src|scenes)/' game --glob '*.gd' || true)" fail_with_matches "production imports prototype code or scenes" "$prototype_imports" test_imports="$(rg -n 'res://tests/' game --glob '*.gd' || true)" fail_with_matches "production imports test code" "$test_imports" poc_references="$(rg -n 'poc_[ab]|POC_[AB]' game --glob '*.gd' --glob '*.tscn' || true)" fail_with_matches "production contains a prototype dependency" "$poc_references" if [ -d game/domain ]; then domain_nodes="$( rg -n '^extends[[:space:]]+(Node|Node2D|Node3D|Control|CanvasLayer|CharacterBody3D|SubViewport)' \ game/domain --glob '*.gd' || true )" fail_with_matches "domain extends scene-tree or presentation nodes" "$domain_nodes" domain_dependencies="$( rg -n 'res://game/(application|world|presentation|infrastructure|composition)/' \ game/domain --glob '*.gd' || true )" fail_with_matches "domain depends on an outer layer" "$domain_dependencies" fi if [ -d game/application ]; then application_dependencies="$( rg -n 'res://game/(world|presentation|infrastructure|composition)/' \ game/application --glob '*.gd' || true )" fail_with_matches "application depends on an adapter layer" "$application_dependencies" fi if [ -d game/world ]; then world_dependencies="$( rg -n 'res://game/(presentation|infrastructure|composition)/' \ game/world --glob '*.gd' || true )" fail_with_matches "world depends on presentation or infrastructure" "$world_dependencies" fi if [ -d game/presentation ]; then presentation_dependencies="$( rg -n 'res://game/(world|infrastructure|composition)/' \ game/presentation --glob '*.gd' || true )" fail_with_matches "presentation depends on a concrete adapter" "$presentation_dependencies" fi if [ -d game/infrastructure ]; then infrastructure_dependencies="$( rg -n 'res://game/(world|presentation|composition)/' \ game/infrastructure --glob '*.gd' || true )" fail_with_matches "infrastructure depends on world or presentation" "$infrastructure_dependencies" fi input_violations="$( rg -n '\bInput\.' game --glob '*.gd' \ | rg -v '^game/infrastructure/input/' \ || true )" fail_with_matches "direct Input access outside infrastructure/input" "$input_violations" fi if [ "$failure_count" -ne 0 ]; then printf 'Architecture checks: %d failure(s).\n' "$failure_count" exit 1 fi printf 'Architecture checks: passed.\n'