Rules for making headless Godot (4.2+) development reproducible, avoiding drift and environment-specific failures.
Scope:
- Godot 4.2+ (Godot 3.x is out of scope)
Required rules (highest priority):
- Always use
--headless --path <PROJECT_DIR>(removescwddependency) - Always capture logs under the project. Use
2>&1 | tee <PROJECT_DIR>/logs/<name>.logfor commands that terminate themselves, and capture Godot's status from${PIPESTATUS[0]}. Whentimeoutis the only termination mechanism, redirect directly with> <PROJECT_DIR>/logs/<name>.log 2>&1and capturetimeout's status explicitly; do not pipe that run throughtee. - Always set project-local
XDG_DATA_HOME,XDG_CONFIG_HOME, andXDG_CACHE_HOMEfor any scripted Godot run. Set all three together — Godot writes to all three categories, and partial redirection still hits the unwritable default for the unset ones. Required to avoid CI/sandbox write failures (Can't open file for writing: ~/.config/godot/...); harmless on a developer machine because the only effect is that per-project metadata/cache live under<PROJECT_DIR>/.godot-xdg/instead of the user's globals. Add.godot-xdg/to.gitignore.- These
exports affect every process started from the same shell, not justgodot. If the same shell session also runs non-Godot tooling that honors XDG vars (e.g. a Node/Playwright browser-automation step for web-export testing), that tooling's own cache/data lookup silently moves under<PROJECT_DIR>/.godot-xdg/too and can fail to find things it installed elsewhere (observed: Playwright reporting a missing browser executable). Export these three vars only in the specific command/subshell that invokesgodot, or explicitly unset/restore them before running unrelated tools in the same shell.
- These
- Never edit
.tscnas raw text (edits must go through--headless --script) - For repeated validation commands, standardize them as
tools/*.sh; after manual tweaks, re-run via the same scripts - If
res://tools/godot_apply_patch.gdis missing, copytools/godot_apply_patch.gd, resolved relative to this loadedSKILL.md, to<PROJECT_DIR>/tools/godot_apply_patch.gdbefore running patch commands - Keep startup smoke and logic tests separate: smoke starts
run/main_scene;run_tests.gdholds project-specific logic checks
Minimum smoke wrapper template
For repeated startup verification, standardize as <PROJECT_DIR>/tools/smoke.sh. Other repeated invocations (run_tests.sh, export_web.sh, ...) follow the same shape — only the godot arguments and the log filename change.
#!/usr/bin/env bash
set -euo pipefail
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
# Project-local XDG (always; see Required rules above).
export XDG_DATA_HOME="$PROJECT_DIR/.godot-xdg/data"
export XDG_CONFIG_HOME="$PROJECT_DIR/.godot-xdg/config"
export XDG_CACHE_HOME="$PROJECT_DIR/.godot-xdg/cache"
mkdir -p "$XDG_DATA_HOME" "$XDG_CONFIG_HOME" "$XDG_CACHE_HOME" "$PROJECT_DIR/logs"
# Smoke = launch run/main_scene headlessly, quit after a few frames.
# --quit-after <N> bounds the run by frame count.
godot --headless --path "$PROJECT_DIR" --quit-after 60 \
2>&1 | tee "$PROJECT_DIR/logs/smoke.log"
echo "godot_exit=${PIPESTATUS[0]}"
Pass criteria for smoke:
- Captured exit code (
${PIPESTATUS[0]}) is0. logs/smoke.logcontains noERROR,SCRIPT ERROR,Failed to load, orParse Errorline outside the project's known-warning whitelist (seereferences/headless_cli.md).- A startup-visible marker is present (e.g. a
print()from the main scene's_ready, or absence of "Main scene can't be loaded").
If you get stuck, provide:
- Full command lines and
logs/*.log godot --versionoutput- Whether
export_presets.cfgexists (when exporting)
Out of scope:
- GUI/OS-specific setup beyond the project-local XDG wrapper
- Level design, render validation, performance tuning
- Game-specific scoring, win/loss rules, controls, and simulation policies
When you need details, read these files (relative to this skill's base directory):
references/headless_cli.md— CLI conventions, XDG setup, known warningsreferences/export_and_import.md— export/import rulesreferences/scene_editing_via_godot.md— safe.tscnediting, patch JSON schema and operationsreferences/testing_headless.md— headless testing strategy
If unsure about Godot CLI or features, consult: