Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@
"source": "./jenkins",
"description": "Observe, operate, and safely diagnose a Jenkins CI/CD server. Read-first; pairs with the Perforce agent's change-commit build loop.",
"keywords": ["jenkins", "ci", "cd", "gamedev", "unreal", "unity"]
},
{
"name": "godot",
"source": "./godot",
"description": "Observe, diagnose, and safely operate a Godot 4 project from the command line - read-first, gamedev-focused. Import-cache doctor, export-preset packing rules, GUT green-run verification, and the GDScript traps no linter catches. Verified against Godot 4.7.1, GDScript only.",
"keywords": ["godot", "godot4", "gdscript", "gamedev", "headless", "export-presets", "gut", "ci"]
}
]
}
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ against our own production depots and builds.
| [**unreal**](./unreal) | Observe, diagnose, and safely operate Unreal Engine build/cook/package pipelines and editor automation - read-first, version-matching doctor, cook-log triage, gated `BuildCookRun`/UBT, first-party editor scripting. | Ready (v0.1.0) |
| [**lore**](./lore) | Observe, analyze, and safely operate an Epic Games [Lore](https://github.com/EpicGames/lore) VCS (the `lore` CLI, formerly Unreal Revision Control) - read-first, staging/commit/push/sync/branch/merge, gated `obliterate` & history rewrites. | Ready (v0.1.0) |
| [**unity**](./unity) | Observe, diagnose, and safely operate a Unity project's CLI build pipeline via the standalone Unity CLI - read-first, Editor-version routing (2022 LTS batchmode vs Unity 6), the batchmode exit-0 trap, gated `unity build`/`test`/`run`, license-vs-auth diagnosis, and the verified Unity 6 Pipeline live-Editor + MCP surface (140 tools, Unity's own confirm/dry_run gates). | Ready (v0.2.0) |
| [godot](./godot) | Godot headless export/build workflows. | Planned |
| [**godot**](./godot) | Observe, diagnose, and safely operate a Godot 4 project from the command line - read-first, `.godot` import-cache doctor, `.uid`/`.import` sidecar hygiene, export-preset packing rules, gated headless import/export, GUT runs with green-run verification, and the GDScript traps no linter catches. Verified against Godot 4.7.1, GDScript only. | Ready (v0.1.0) |
| [reddit](./reddit) | Reddit Devvit app workflows - `devvit` CLI playtest/upload/publish and CI integration for games shipped as Reddit apps. | Planned |
| [youtube](./youtube) | YouTube Playables packaging and pre-submission readiness checks (no public deploy API - Developer Portal uploads are manual). | Planned |
| [**jenkins**](./jenkins) | Observe, operate, and safely diagnose a Jenkins CI/CD server; read-first, built around the Perforce `change-commit` → Jenkins build loop, with gated triggers/aborts and the Script Console refused. | Ready (v0.1.0) |
Expand Down Expand Up @@ -63,11 +63,12 @@ MIT. See [LICENSE](./LICENSE).
Perforce, Helix Core, and P4 are trademarks or registered trademarks of Perforce
Software, Inc. Unreal Engine, Epic Games, and Lore are trademarks or registered
trademarks of Epic Games, Inc. Unity is a trademark or registered trademark of
Unity Technologies. Jenkins is a trademark of the Jenkins project (a
Continuous Delivery Foundation project).
Unity Technologies. Godot is a trademark of the Godot Foundation. Jenkins is a
trademark of the Jenkins project (a Continuous Delivery Foundation project).

ButterStack is not affiliated with, endorsed by, or sponsored by Perforce
Software, Epic Games, Unity Technologies, or the Jenkins project. These marks
Software, Epic Games, Unity Technologies, the Godot Foundation, or the Jenkins
project. These marks
are used only to identify the third-party tools these agents observe and
operate; naming a tool is not a claim of partnership or endorsement.

Expand Down
24 changes: 24 additions & 0 deletions godot/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "godot",
"displayName": "Godot Agent",
"description": "Observe, diagnose, and safely operate a Godot 4 project from the command line. A read-first, gamedev-focused Claude Code agent built around the headless CLI: an import-state DOCTOR (the stale .godot class cache that fails whole suites with phantom errors), export-preset packing rules, GUT's exit-green-on-parse-failure trap, and the GDScript language and lifetime traps that no linter catches. Verified against Godot 4.7.1, GDScript only.",
"version": "0.1.0",
"author": {
"name": "ButterStack"
},
"homepage": "https://github.com/ButterStack/gamedev-agents/tree/main/godot",
"repository": "https://github.com/ButterStack/gamedev-agents",
"license": "MIT",
"keywords": [
"godot",
"godot4",
"gdscript",
"gamedev",
"headless",
"godot-cli",
"export-presets",
"gut",
"class-cache",
"ci"
]
}
55 changes: 55 additions & 0 deletions godot/LEARNINGS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Godot Agent - Learnings (running log)

Companion to [`NOTES.md`](./NOTES.md) (design rationale and validation status).
Two kinds of learning, kept separate: **(A)** findings that shaped what the
skills say, and **(B)** the operational reality of running Godot headlessly in
CI and in worktrees. Full detail lives in the skills; this log records why
they say what they say. No secrets.

## 2026-08-25 - v0.1.0, distilled from Pilot Light

Everything below comes from building and shipping Pilot Light, ButterStack's
Godot 4.7.1 shmup, to Android and iOS through CI.

### A. Agent / skill learnings

- **Import state outranks everything else in a Godot doctor.** A stale
`global_script_class_cache.cfg` after a branch switch produced 31 failing
tests on a commit that was a green tip of main; one `--headless --import`
took it to 271/271 with no other change. So the doctor leads with cache
state, and the failure-signature table's first row maps "burst of
could-not-find-type errors" straight to it. *(⤳ `godot-observe` §3, §7.)*
- **A green test run needed its own trust model.** Two independent mechanisms
produce a green exit while real failures happen, and they compose: GUT
cannot distinguish a parse failure from a non-GutTest file (exit 0), and
GitHub Actions' default `bash -e {0}` has no `pipefail`, so any `| tee` gate
reports `tee`'s status. Both were live simultaneously across multiple
merges - which is why `godot-test` is a skill. *(⤳ `godot-test` §1, §3.)*
- **Several real defects are invisible to every cheap gate.** A native-method
shadow that killed all collision on device passed `gdlint`, `--import`, a
boot check, and the pure-math suites; only a scene-tree integration test
caught it. Hence the explicit gate-coverage table in `godot-test` §4.
- **Honest non-reproduction is worth recording.** The skills carry only the
reproduced class-cache trigger, not a plausible-sounding explanation that
was quoted but never reproduced.

### B. Operational / rig learnings

1. **A first `--import` in a fresh worktree is a git hazard**: roughly 150
untracked `.uid`/`.import` files that a `git add -A` would sweep into a
feature PR. Hence stage-by-filename and the guard hook. *(⤳ `godot-observe` §4.)*
2. **Import ordering around a merge is destructive.** `--import` before a
merge creates untracked files the incoming branch also carries; git refuses
("untracked working tree files would be overwritten by merge") and the
merge aborts with **no conflict list**. Import after merging, never before.
*(⤳ `godot-build` §1.)*
3. **`--headless` cannot render, and fails by hanging rather than erroring.**
A scripted screenshot via `SubViewport.get_texture().get_image()` sits at
near-zero CPU forever; no flag fixes it. *(⤳ `godot-build` §4.)*
4. **Only an export proves packing.** Editor and headless runs read the
project directory, not the PCK - a CI-generated plain text file read fine
everywhere except on device, because `export_filter="all_resources"` does
not sweep non-resource files. *(⤳ `godot-observe` §5, `godot-build` §2.)*
5. **Pin the engine.** All of the above was validated against
`barichello/godot-ci:4.7.1`, matching CI exactly; a local result and a
CI result that disagree are usually a version difference.
57 changes: 57 additions & 0 deletions godot/NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Godot Agent - design notes and validation status

## Why these four skills

The split follows the same observe / operate / verify shape as the other
plugins, with one addition:

- **`godot-observe`** is the anchor, as `p4-observe` and `unreal-observe` are in
theirs. Nearly every confusing Godot failure traces back to import state, so
the doctor leads with the `.godot/` cache rather than with the engine binary.
- **`godot-build`** holds anything that writes. `--headless --import` lives here
rather than in observe precisely because it mutates the project.
- **`godot-test`** exists as its own skill because the strongest cluster of
findings is not "how to run tests" but "why a green run is lying", which is
too big to bury in a section of another skill.
- **`godot-gdscript`** is the one departure from the three-skill shape. The
language and lifetime traps are about reading and writing code rather than
operating a rig, and there are enough of them (nine, several of which shipped
as real defects) to stand alone.

## Validation status

**Verified against Godot 4.7.1, GDScript only**, on Pilot Light - a vertical
shmup built AI-end-to-end, shipping Android and iOS through CI, with a GUT
suite in the 270-400 test range and the engine pinned via
`barichello/godot-ci:4.7.1`, so the findings come from a reproducible engine.

### Verified against a real engine and project

Ten findings, each with a deterministic repro or an on-device confirmation:
the stale-class-cache suite failure (31 failures to 271/271 on one import),
both green-run lies (GUT's silent skip and `tee` under `bash -e {0}`), the
native-method shadow and the unpacked plain non-resource file caught only on
device, the merge aborting with no conflict list, the `--headless`
`get_image()` hang, the dropped `RefCounted` signal target, 4.7's constant
checker, and the timer-residual quantization. Exact strings and numbers live
in the skills and [`LEARNINGS.md`](./LEARNINGS.md).

### Not verified - do not imply otherwise

- **Any engine version other than 4.7.1.** Several findings are explicitly 4.7
static-analyzer or constant-checker rules; none of this has been re-run on a
4.2/4.3 LTS build, where they may simply not apply.
- **C# / Mono.** Every finding is GDScript. The `.godot` cache and
export-preset behaviors are plausibly engine-general, but untested.
- **Web export.** `godot-build` deliberately says nothing about it; the
validation project has no Web preset, so there is no basis for a claim.
- **Godot 3.x.** Out of scope entirely.
- **Whether the `.uid` sidecar flood generalizes.** Observed on a repo whose
`.gitignore` has no `*.uid`/`*.import` pattern; a project that ignores or
commits them will not see it.

### Deliberately not vendored

Pilot Light's boot-check script and allowlist are project-shaped, so the
boot-check *pattern* is described in `godot-test` §4 instead; the shipped
`godot-export-retry.sh` is generic (preset and output path as arguments).
66 changes: 61 additions & 5 deletions godot/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,66 @@
# Godot Agent (planned)
# Godot Agent

A ButterStack gamedev agent for **Godot** workflows - headless export/build
(`--export-release`), `.import/` and `.godot/` cache handling, and CI integration.
A ButterStack gamedev agent for **Godot 4** - observe, diagnose, and safely
operate a project from the command line: the `.godot/` import cache and the
phantom test failures a stale one produces, `.uid`/`.import` sidecar hygiene,
export-preset packing rules, gated headless import/export, GUT runs and the two
ways a green suite is lying, and the GDScript traps that survive every linter.

> 🚧 **Placeholder - not yet implemented.** Part of the ButterStack gamedev agents
> series. See [`perforce`](../perforce) for the first shipped agent.
> Part of the series of public gamedev agents from ButterStack, alongside
> [`perforce`](../perforce), [`unreal`](../unreal), [`unity`](../unity),
> [`lore`](../lore), and [`jenkins`](../jenkins).

> **Verified against Godot 4.7.1, GDScript only.** No C#/Mono coverage, and no
> claim about 4.2/4.3 LTS - several findings are explicitly 4.7 rules. Findings
> were validated on Pilot Light, ButterStack's vertical shmup built
> AI-end-to-end in Godot and shipping Android and iOS builds through CI.

## Skills

| Skill | Use for |
|---|---|
| **`godot-observe`** | Read-only doctor: engine/project version match, `.godot/` cache state, sidecar hygiene, export presets, log diagnosis, and a failure-signature table. |
| **`godot-build`** | Gated `--headless --import` and `--export-release`/`--export-debug`, the merge-ordering rule, Android/iOS notes, and the headless rendering limits. |
| **`godot-test`** | GUT runs, and the two independent ways a suite exits green while real failures happen. |
| **`godot-gdscript`** | Language and object-lifetime traps that pass lint, import, and boot checks - and in one case shipped. |

## Commands

- `/godot-doctor [path]` - read-only project diagnosis
- `/godot-build [import|export] [preset]` - gated import or export
- `/godot-test [path]` - run GUT and verify the result is trustworthy

## The three findings worth reading first

1. **A stale `.godot/` class cache fails whole suites with phantom errors.** The
cache is gitignored, does not travel with a checkout, and nothing
invalidates it on a branch switch. One observed case: 31 failing tests on a
commit that was a green tip of main, fixed to 271/271 by a single
`--headless --import` with no other change. Suspect the cache before the
code.
2. **A green GUT run is not evidence the tests ran.** GUT cannot tell "this
script failed to parse" from "this script isn't a GutTest" - it logs one
`Ignoring script ...` line and exits 0. Separately, `tee` without `pipefail`
makes every piped CI gate unfailable. Both were live in a real repo for
multiple merges.
3. **Never shadow a native method.** GDScript has no overloading, so a
same-named script method collides with the native one even at a different
arity. A pool's `get_position(index)` colliding with `Node2D.get_position()`
silently killed all collision detection on device, and no gate caught it.

## Safety

A `PreToolUse` hook (`scripts/guard-godot.sh`) hard-blocks removing
`project.godot`/`export_presets.cfg`, deleting `.import` sidecars on their own
(Godot's `.meta` analog), and unscoped `git clean -f`. It deliberately allows
`rm -rf .godot`, which is regenerable and a legitimate fix.

## Install

```
/plugin marketplace add ButterStack/gamedev-agents
/plugin install godot@gamedev-agents
```

---

Expand Down
64 changes: 64 additions & 0 deletions godot/agents/godot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
name: godot
description: >
Use this agent to observe, diagnose, and safely operate a Godot 4 project
from the command line in game development: resolving the engine binary and
matching it against the project's `config/features`, diagnosing the `.godot/`
import cache (whose staleness after a branch switch fails whole test suites
with phantom "could not find type" errors), `.uid`/`.import` sidecar hygiene
and the `git add -A` hazard they create, export-preset packing rules
(`export_filter="all_resources"` does not pack plain non-resource files),
gated `--headless --import` and `--export-release`/`--export-debug`, the
merge-ordering rule that makes an early import abort a merge outright, GUT
test runs and the two independent ways a suite exits green while failing,
and the GDScript language and object-lifetime traps no linter catches.
Invoke when the user mentions Godot, GDScript, a project.godot, an
export_presets.cfg, a .tscn/.tres/.gd file, the .godot cache, .uid/.import
sidecars, GUT, gdformat/gdlint, `--headless`, or asks to import, export,
test, or diagnose a Godot project. Prefer this agent over ad-hoc shell
commands whenever the repo contains a `project.godot`.
tools: Bash, Read, Edit, Write, Grep, Glob
model: sonnet
---

# Godot Agent - ButterStack Gamedev Series

Read-first. Diagnose before you touch anything, and prefer the cheapest command
that answers the question.

**Verified against Godot 4.7.1, GDScript only.** No C#/Mono coverage, and no
claim about 4.2/4.3 LTS. Several findings are explicitly 4.7 rules. Say which
engine version produced any result you report - a build or test claim without
its engine version is not reproducible.

## Skills

| Skill | Use for |
|---|---|
| `godot-observe` | read-only doctor: engine/project version match, `.godot/` cache state, sidecar hygiene, export presets, log diagnosis, failure-signature table |
| `godot-build` | gated `--headless --import` and export, the merge-ordering rule, platform notes, headless rendering limits |
| `godot-test` | GUT runs, and the two ways a green exit is lying |
| `godot-gdscript` | language and lifetime traps that survive lint, import, and boot checks |

## Operating rules

1. **Doctor first.** `godot-observe` §1-§3. An engine/project version mismatch
or a stale `.godot/` cache makes every downstream error misleading.
2. **Suspect the cache before the code.** A burst of "could not find type"
errors, or many tests failing with only generic "Unexpected Errors", is a
stale class cache until proven otherwise. One `--headless --import` settles
it.
3. **Import after a merge, never before.** An early import generates the very
sidecars the incoming branch carries and aborts the merge with no conflict
list at all.
4. **Never `git add -A` in a Godot repo.** A first import in a fresh worktree
leaves ~150 untracked `.uid`/`.import` files. Stage by explicit filename.
5. **Do not trust a green test run on its own.** GUT exits 0 on a script it
could not parse, and `tee` without `pipefail` makes every CI gate unfailable.
Check that the suite actually collected what it should have.
6. **Only a real export proves packing.** Editor and headless runs read the
project directory, not the PCK. Label that evidence honestly.
7. **Gate the expensive things.** Show the exact command and its cost estimate
before an export, and wait for confirmation.
8. **No screenshots under `--headless`.** `get_image()` on a viewport texture
hangs forever; there is no flag that fixes it.
24 changes: 24 additions & 0 deletions godot/commands/godot-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
description: Import or export a Godot 4 project (gated) - headless import, export-release/debug against a preset, with cost shown before running
argument-hint: "[import|export] [preset-name]"
---

Drive a Godot import or export for the user, per `$ARGUMENTS`. Use the
`godot-build` skill for exact command forms and `godot-observe` for the
preflight.

Before running anything:

1. Run the doctor preflight (`godot-build` §0). Do not proceed past an
engine/project version mismatch without saying so.
2. **If a merge is pending, stop** and apply the ordering rule (`godot-build`
§1): import *after* merging, never before, or the merge aborts with no
conflict list.
3. **Show the exact command and a cost estimate, then wait for confirmation**
before any export. An import is cheap enough to run without ceremony unless
the merge case applies.

After running: report the exit status, the engine version, and the first real
error lines rather than just "it failed". For an export, state the artifact
path and note that only this export proves packing - an editor or headless run
against the project directory does not.
Loading
Loading