agentleFS
vl4dtvl4dt/godot-skills/skills/godot-headless-workflow/SKILL.md

godot-headless-workflow

Skill8 starsChanged 5 months agoMITView on GitHub

AI-agent development loop for Godot 4.x without opening the editor: headless validation (--check-only), project import, gdUnit4 testing, safe hand-editing of text scenes (.tscn), CLI export pipeline for Android/iOS, mobile checklist, and RevenueCat plugin integration. Use when building or modifying a Godot game as a coding agent, running Godot in CI, validating GDScript changes, exporting mobile builds, or integrating in-app purchases.

The file

We split this skill into the rules it gives an agent

10 rules. About 0 are common, 6 are distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈6

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    The editor GUI is optional: Use it (or the package's MCP bridge) only for visual layout iteration.

    line 40
  2. Very specific:

    All Godot CLI flags, gdUnit4 runners (runtest.cmd / runtest.sh), res:// paths, and export behavior are identical across hosts — projects …

    line 60
  3. Very specific:

    Line endings kill .sh scripts: if verify.sh is checked out with CRLF on Linux it fails with $'\r': command not found.

    line 63
  4. Distinctive:

    Don't mix toolchain worlds: run Godot + Android SDK/adb natively per OS.

    line 68
  5. Very specific:

    Run this before anything else in a fresh clone, and whenever you added textures/scenes/fonts.

    line 93
  6. Distinctive:

    NOT catch broken node paths, missing autoloads, or runtime type errors — those need steps 3–4.

    line 103
  7. Distinctive:

    When structural changes get risky (new node trees, wiring signals between nodes), generate the scene programmatically instead:

    line 192
  8. Very specific:

    Use the community Godotx RevenueCat plugin (MIT.

    line 256
  9. Distinctive:

    Create the app + IAP products in Play Console / App Store Connect first, mirror them in the RevenueCat dashboard, note the …

    line 261
  10. Distinctive:

    Test a real sandbox purchase on device before building game systems on top of it

    line 264

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Godot Headless Workflow (Agent Development Loop)

The complete edit→validate→test→run→export loop for Godot projects without ever opening the editor. Every change must pass the headless gate before you claim it works.

Golden Rules

  1. Never trust an edit you haven't validated headlessly. A script that looks right but was never parsed is not done.
  2. GDScript only unless the project already uses C# (see godot-csharp-patterns). Pin the engine version in the project's AGENTS.md (e.g., Godot 4.7.2 stable, GDScript).
  3. Scenes are text (.tscn/.tres) — editable by hand with care, or better, generated by tool scripts. See §Text Asset Safety.
  4. Never touch .godot/ — it's a regenerable cache.
  5. The editor GUI is optional. Use it (or the package's MCP bridge) only for visual layout iteration; logic, tests, and builds are all CLI work.

Prerequisites

godot --version          # e.g. 4.7.stable.official — confirm once per machine/session

The Godot binary must be on PATH in the shell you run commands from (Git Bash on Windows, native bash on Linux). Export templates are needed only for §CLI Export:

HostTemplate install path
Windows%APPDATA%\Godot\export_templates\<engine-version>\
Linux~/.local/share/godot/export_templates/<engine-version>/
macOS~/Library/Application Support/Godot/export_templates/<engine-version>/

Cross-Platform (Windows ↔ Ubuntu hosts)

All Godot CLI flags, gdUnit4 runners (runtest.cmd / runtest.sh), res:// paths, and export behavior are identical across hosts — projects stay fully portable. Watch only:

  1. Line endings kill .sh scripts: if verify.sh is checked out with CRLF on Linux it fails with $'\r': command not found. Add to every game repo's .gitattributes: *.sh text eol=lf (and commit it from day one).
  2. Runner extension differs by host — runtest.cmd on Windows, runtest.sh elsewhere; detect or parameterize instead of hardcoding one.
  3. Don't mix toolchain worlds: run Godot + Android SDK/adb natively per OS. Building inside WSL while your emulator/adb runs on Windows causes device-detection pain. On Ubuntu, USB debugging needs udev rules for your phone vendor before adb devices lists it.
  4. Signing env vars are the same on both hosts — just use absolute paths appropriate per filesystem in GODOT_ANDROID_KEYSTORE_PATH.

The Core Loop

edit (.gd / .tscn / project settings)
  → full import            (after adding/removing assets or scenes)
  → parse check            (every changed script)
  → unit tests             (gdUnit4, see references/testing.md)
  → smoke run              (boot the game headlessly for N frames)
  → commit                 (only when everything above exits 0)
  → export                 (when feature-complete; see references/exporting.md)

Step 1 — Full import (required after asset/scene changes)

godot --headless --path . --import

Imports all resources, generates .godot/ cache, resolves UIDs, and reports load errors. Run this before anything else in a fresh clone, and whenever you added textures/scenes/fonts. Exit code ≠ 0 means broken resources — fix before continuing.

Step 2 — Parse-check changed scripts

godot --headless --check-only --script res://scripts/player.gd

Syntax + basic type checking without executing. Fast enough to run per-edit. Note: it does NOT catch broken node paths, missing autoloads, or runtime type errors — those need steps 3–4.

Step 3 — Unit tests (gdUnit4)

Standardize every project on gdUnit4. Tests live in res://tests/; suites extend GdUnitTestSuite. Run from the project root:

# Windows
addons/gdUnit4/runtest.cmd -a res://tests --continue
# Linux/macOS
./addons/gdUnit4/runtest.sh -a res://tests --continue

Non-zero exit = failures (CI-safe). HTML/XML reports land in res://reports/. Full patterns — assertions, async tests, parametrized cases — in references/testing.md.

Step 4 — Smoke run

Boot the real game headlessly and let it tick a bounded number of frames:

godot --headless --path . --quit-after 300   # ~300 frames of main scene

Runtime errors (bad autoload order, missing nodes, signal typos) print here and fail loudly. For deeper checks (instantiate specific scenes, assert state), use a SceneTree driver script:

# tools/smoke_test.gd — instantiate target scenes, tick frames, assert they survive
extends SceneTree

func _init() -> void:
	call_deferred("_run")

func _run() -> void:
	var targets := ["res://scenes/main.tscn"]
	var errors := 0
	for path in targets:
		var ps: PackedScene = load(path)
		if ps == null:
			push_error("FAILED TO LOAD: " + path)
			errors += 1
			continue
		var node := ps.instantiate()
		root.add_child(node)
		await process_frame
		await process_frame
		node.queue_free()
		await process_frame
	print("SMOKE RESULT: ", "FAIL" if errors > 0 else "PASS")
	quit(1 if errors > 0 else 0)
godot --headless --path . -s res://tools/smoke_test.gd

Copy-paste verification gate (put this in every game repo)

#!/usr/bin/env bash
# verify.sh — the agent gate. Run after EVERY batch of edits. All steps must exit 0.
set -e
godot --headless --path . --import
for f in $(git diff --name-only HEAD | grep '\.gd$'); do
  godot --headless --check-only --script "res://${f#*/}" || exit 1
done
addons/gdUnit4/runtest.cmd -a res://tests --continue      # Linux/macOS: runtest.sh
godot --headless --path . --quit-after 300
echo "ALL CHECKS PASSED"

Text Asset Safety (.tscn / .tres)

Scenes and resources are plain text — readable, diffable, mergeable. Rules:

DoDon't
Read .tscn to learn structure before editing scriptsInvent uid:// values
Edit node properties, positions, exported values in placeDelete or rename .uid sidecar files
Add [node] blocks mirroring existing syntax exactlyReference resources by UID you haven't seen
Prefer generating scenes via a tool script (below)Hand-write complex nested scenes from scratch
Let Godot regenerate paths after moving files (--import)Edit anything under .godot/

Since Godot 4.4, scripts carry .uid sidecars and ext_resource entries may point at uid://…. If both uid and path exist, either resolving is fine — keep the path attribute when hand-editing and never fabricate UIDs.

When structural changes get risky (new node trees, wiring signals between nodes), generate the scene programmatically instead:

# tools/build_scene.gd — run: godot --headless --path . -s res://tools/build_scene.gd
extends SceneTree

func _init() -> void:
	call_deferred("_build")

func _build() -> void:
	var root_node := Node2D.new()
	root_node.name = "Level1"
	var sprite := Sprite2D.new()
	sprite.name = "Player"
	sprite.position = Vector2(640, 360)
	root_node.add_child(sprite)

	var packed := PackedScene.new()
	packed.pack(root_node)
	ResourceSaver.save(packed, "res://scenes/levels/level_1.tscn")
	print("SCENE SAVED")
	quit(0)

This is deterministic, reviewable, and can't corrupt unrelated scene sections.

CLI Export

One-time setup: create presets (editor UI or by writing export_presets.cfg — see references/exporting.md for working Android/iOS preset skeletons).

# Windows/Linux desktop
godot --headless --path . --export-release "Windows Desktop" builds/win/game.exe

# Android (AAB for Play Store; APK for sideload/Galaxy Store)
godot --headless --path . --export-release "Android" builds/android/game.aab

# iOS — produces an Xcode project FOLDER, archived later on macOS
godot --headless --path . --export-release "iOS" builds/ios/

Release Android exports read signing config from env vars:

export GODOT_ANDROID_KEYSTORE_PATH=/path/to/release.keystore
export GODOT_ANDROID_KEYSTORE_USER=alias
export GODOT_ANDROID_KEYSTORE_PASSWORD=secret

Details, debugging exports (--export-debug), and the store-submission checklist live in references/exporting.md.

Mobile Quick Checklist

  • Stretch mode canvas_items + portrait/landscape locked in project.godot
  • Touch input: enable "Emulate Mouse From Touch"; consider Godot 4.7's VirtualJoystick
  • Respect notches/safe areas (get visible rect from viewport, anchor UI inside it)
  • Budget: mid-range phone should hold 60 fps — profile with godot-performance skill before shipping
  • Min versions when using the RevenueCat plugin below: iOS 15+, Android SDK 24+

In-App Purchases (RevenueCat)

Use the community Godotx RevenueCat plugin (MIT; wraps the official RC iOS/Android SDKs; built against Godot 4.7-stable). Condensed flow — full playbook in references/revenuecat.md:

  1. Install via AssetLib ("Godotx RevenueCat") → gives addons/, ios/plugins/, android/plugins/
  2. Enable the plugin per export preset; Android additionally needs Gradle Build ON
  3. Create the app + IAP products in Play Console / App Store Connect first, mirror them in the RevenueCat dashboard, note the platform-specific public API keys
  4. Init at startup with the platform key → fetch offerings → paywall → purchase → entitlement check
  5. Test a real sandbox purchase on device before building game systems on top of it

⚠️ Verify exact method names against the addon's own README/API reference at integration time — don't guess API surface (see failure modes below).

Agent Failure Modes (catch these in review)

Failure modeSymptomGate
Godot 3 API bleedonready var, yield(, .instance(), tool alone, export var, string-form connect("x", self, "y")`grep -rnE "onready |yield(.instance()^tool\b^export |connect("" scripts/` → must be empty
Unity API bleed (C# projects)UnityEngine, MonoBehaviour, GetComponentOnly relevant in .NET projects; prefer GDScript to avoid entirely
Invented UIDs / broken refsLoad errors at importRun --import gate after every scene edit
Autoload order bugsSingleton accessed before init in _init()Move access to _ready(); smoke run catches it
Wrong callbackGame logic in _process() that needs physics ticksUse _physics_process() for movement/forces (godot-physics skill)
Signal leaksConnections to freed nodes; crashes on scene changeConnect in _ready(), disconnect or use CONNECT_ONE_SHOT; review with godot-code-review skill
Guessed external APIsHallucinated plugin/engine methodsOnly cite APIs found in the project's addons/docs or verified via --check-only passing

Editor vs Headless — When To Use Which

TaskTool
Script edits, tests, exports, CI, refactorsHeadless CLI (this skill)
Visual layout polish, animation curves, theme tweakingEditor GUI (human) or the package's mcp-bridge/ MCP server for agent-driven editor control
Import cache issuesAlways --import headless, never hand-fix .godot/

Related Skills

NeedSkill
Scaffold the projectgodot-project-setup
GDScript style & architecture decisionsgodot-gdscript-patterns, godot-brainstorming
Review before merging featuresgodot-code-review
Diagnose failing smoke runsgodot-debugging
Ship fast on phonesgodot-performance
Input handling, save systems, audio, shadersgodot-input, godot-save-systems, godot-audio, godot-shaders-vfx

References

  • references/testing.md — gdUnit4 deep dive: suites, assertions, async, CI integration
  • references/exporting.md — export presets, Android/iOS pipelines, store submission checklist
  • references/revenuecat.md — RevenueCat plugin integration playbook for mobile games

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.