diff --git a/Makefile b/Makefile index 04ca7bc27..bc7103b23 100644 --- a/Makefile +++ b/Makefile @@ -70,6 +70,8 @@ setup-tooling: @command -v python3 >/dev/null 2>&1 || { echo "Install Python 3"; exit 1; } @command -v curl >/dev/null 2>&1 || { echo "Install curl (required for Godot download)"; exit 1; } @command -v unzip >/dev/null 2>&1 || { echo "Install unzip (required for Godot download)"; exit 1; } + @python3 -c "import yaml" 2>/dev/null || { echo "Install PyYAML: pip install pyyaml"; exit 1; } + @python3 -c "import jsonschema" 2>/dev/null || { echo "Install jsonschema: pip install jsonschema"; exit 1; } setup-hooks: @git config core.hooksPath .config/hooks @@ -130,9 +132,11 @@ golden-diff: @echo "Regenerating golden output for comparison..." @mkdir -p .cache/golden @cp server/tests/golden/proof_room_tick_10.json .cache/golden/before.json - @cd server && UPDATE_GOLDEN=1 cargo test --test golden_suite -- proof_room_tick_10_matches_golden - @cp server/tests/golden/proof_room_tick_10.json .cache/golden/after.json - @cp .cache/golden/before.json server/tests/golden/proof_room_tick_10.json + @cd server && UPDATE_GOLDEN=1 cargo test --test golden_suite -- proof_room_tick_10_matches_golden; \ + rc=$$?; \ + cp ../server/tests/golden/proof_room_tick_10.json ../.cache/golden/after.json 2>/dev/null; \ + cp ../.cache/golden/before.json ../server/tests/golden/proof_room_tick_10.json; \ + if [ $$rc -ne 0 ]; then echo "Cargo test failed (golden file restored)."; exit $$rc; fi @if diff -q .cache/golden/before.json .cache/golden/after.json >/dev/null 2>&1; then \ echo "--- Golden file: UP TO DATE ---"; \ else \ @@ -229,7 +233,7 @@ pre-pr-server: lint-server build-server test-server pre-pr-fixtures pre-pr-client: lint-client build-client test-client @echo "=== Client pre-PR: PASSED ===" -pre-pr-content: validate-content check-fact-ids +pre-pr-content: validate-content check-fact-ids checklist-validate @echo "=== Content pre-PR: PASSED ===" # --- CI (run locally) --- diff --git a/content/_schema/checklist.schema.json b/content/_schema/checklist.schema.json index 4b05f272d..ca8ae582a 100644 --- a/content/_schema/checklist.schema.json +++ b/content/_schema/checklist.schema.json @@ -17,6 +17,11 @@ "minLength": 1, "description": "Human-readable room name" }, + "scope": { + "type": "string", + "enum": ["per_room", "cross_room"], + "description": "Checklist scope: per_room (default) or cross_room" + }, "description": { "type": "string" }, @@ -40,7 +45,7 @@ "condition_id": { "type": "string", "pattern": "^[a-z][a-z0-9_-]*$", - "description": "Unique condition identifier within this checklist" + "description": "Globally unique condition identifier across all checklists. Use room_id prefix (e.g., inv-item-present, occ-wall-visible)." }, "player_near": { "type": "object", diff --git a/content/gauntlet/cross_room_checks.yaml b/content/gauntlet/cross_room_checks.yaml index 6890cd850..12e49ef58 100644 --- a/content/gauntlet/cross_room_checks.yaml +++ b/content/gauntlet/cross_room_checks.yaml @@ -1,3 +1,4 @@ +scope: cross_room description: "Cross-room checks applied to every Gauntlet room visit." conditions: - id: cross-player-entity-present diff --git a/tooling/validate-checklist b/tooling/validate-checklist index 30a0699fc..727985087 100755 --- a/tooling/validate-checklist +++ b/tooling/validate-checklist @@ -21,6 +21,10 @@ GAUNTLET_DIR = ROOT / "content" / "gauntlet" def load_schema(): + if not SCHEMA_PATH.exists(): + print(f"ERROR: Schema file not found at {_rel(SCHEMA_PATH)}") + print("Expected: content/_schema/checklist.schema.json") + sys.exit(1) with open(SCHEMA_PATH) as f: return json.load(f)