Skip to content

Commit a97acc2

Browse files
dsarnoclaude
andauthored
docs: sync with recent PRs (4.3 compat #476/#478, game domain #470) (#480)
* docs: capture Godot 4.3 compat + CI canary work (#476, #478) Reflect this session's 4.3 work across the doc set: - CLAUDE.md: new "Godot version support (4.3+)" section — the Engine.call() forward-compat pattern for newer-than-4.3 APIs, the benign `extends Logger` parse errors on < 4.5, and the open 4.3 self-update crash (#475) with the manual-swap workaround and the in-tree-backup foot-gun. Updated the CI known-issues bullets for the ALLOW_LOGGER_PARSE_ERRORS filter, the 4.3.0 canary row, and the skip_on_godot_lt / SKIP_POSTCHURN_TEST_RUN skips. - skill.md: forward-compat + Logger GDScript conventions; version-gated test skips + 4.3 canary in the testing section. - implementation-plan.md: CI-coverage item now lists the 4.3 canary; compatibility item notes 4.3 load restored + the open self-update gap. - testing-strategy.md: 4.3 canary in the CI tiers; ci-check-gdscript Logger-filter env gate. Docs only; no code or tool-surface changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: sync game domain (#470) into skill + plan inventories Going back a few PRs surfaced drift the 4.3-focused pass missed: PR #470 added the `game_manage` rollup (running-game inspection + synthetic input) but the skill's inventory and the implementation plan never recorded it. (docs/TOOLS.md was already current.) - skill.md: add `game` to the tools-module list; add the `game_manage` row to the rollup table; correct the tool count (~39 -> ~40, 4 core + 15 named + 21 rollups). Rollup table now matches the 21 rollups registered in src/godot_ai/tools/ (test_tool_domains parity). - implementation-plan.md: record `game_manage` under Runtime Feedback Loop (get_scene_tree / get_node_info / input_* routed to the live game process via game_eval / game_command + the _mcp_game_helper autoload). PRs #471/#472/#473 were Windows-only test fixes with no doc surface. Docs only. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6367717 commit a97acc2

4 files changed

Lines changed: 24 additions & 9 deletions

File tree

.claude/skills/godot-ai/skill.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ globs:
1212

1313
- `src/godot_ai/` — Python MCP server (FastMCP v3)
1414
- `server.py` — entrypoint, lifespan, tool registration, `--exclude-domains` support
15-
- `tools/` — MCP tool modules (session, editor, scene, node, project, script, resource, filesystem, signal, autoload, input_map, testing, batch, client, ui, theme, animation, material, particle, camera, audio) + `_meta_tool.py` (`register_manage_tool` rollup factory)
15+
- `tools/` — MCP tool modules (session, editor, scene, node, project, script, resource, filesystem, signal, autoload, input_map, game, testing, batch, client, ui, theme, animation, material, particle, camera, audio) + `_meta_tool.py` (`register_manage_tool` rollup factory)
1616
- `resources/``godot://...` read-only URIs (sessions, editor, project, nodes, scripts, scenes, library)
1717
- `middleware/``PreserveGodotCommandErrorData`, `StripClientWrapperKwargs`, `ParseStringifiedParams`, `HintOpTypoOnManage` (registration order is load-bearing — see the docstring above the `mcp.add_middleware(...)` calls in `server.py` and `tests/unit/test_server_middleware_order.py`)
1818
- `handlers/` — shared sync handlers using `DirectRuntime`; `_readiness.py` gates writes
@@ -78,6 +78,8 @@ Run Godot tests: use `run_tests` MCP tool (no reload needed for test file edits)
7878

7979
Test guardrails: the runner flags tests with 0 assertions as failures (catches silent `return` before asserting). Always use `assert_true(false, "reason")` before early `return` in test methods. Test discovery is resilient — a broken `.gd` file doesn't kill discovery of the rest.
8080

81+
Version-gated skips: for a test that depends on 4.4+-only behavior, call `if skip_on_godot_lt("4.4", "reason"): return` at the top (`McpTestSuite.skip_on_godot_lt` returns `bool`). CI runs a Godot 4.3 Linux canary (`Godot tests / Linux (Godot 4.3)`, pinned to `4.3.0`) in addition to the three 4.6.2 OS rows; the canary sets `ALLOW_LOGGER_PARSE_ERRORS=1` (suppresses the benign `extends Logger` parse errors in `ci-check-gdscript`) and `SKIP_POSTCHURN_TEST_RUN=1` (the reload smoke's post-churn `test_run` outruns the 30s curl timeout on slow 4.3 GDScript exec).
82+
8183
## GDScript conventions
8284

8385
- Handlers are `@tool` `RefCounted` scripts with **no** `class_name` — load them via `const X := preload("res://addons/godot_ai/handlers/foo_handler.gd")` from `plugin.gd`. The `Mcp*`-prefixed `class_name` is reserved for utility classes shared across the project (e.g. `McpScenePath`, `McpPropertyErrors`, `McpParamValidators`); see #253 for why bare `class_name`s on handlers are forbidden.
@@ -89,6 +91,8 @@ Test guardrails: the runner flags tests with 0 assertions as failures (catches s
8991
- Use `McpScenePath.from_node()` / `McpScenePath.resolve()` for clean paths like `/Main/Camera3D`
9092
- Use `##` for doc comments, typed arrays (`Array[String]`), never Python-style `"""`
9193
- Main thread only — 4ms frame budget in `_process()`, use `call_deferred` for mutations
94+
- **Forward-compat engine APIs**: call newer-than-4.3 engine methods via `Engine.call("method", ...)` / `OS.call("method", ...)`, never a direct reference. A direct `Engine.capture_script_backtraces(...)` is rejected by the 4.3 *parser* (type-checked against the native class) even behind an `Engine.has_method(...)` runtime guard, cascading to a full plugin-load failure on 4.3 (#476). `Engine.call(...)` is identical at runtime on 4.4+ but invisible to the older parser.
95+
- **`extends Logger` is 4.5+**: `runtime/editor_logger.gd` / `game_logger.gd` extend `Logger` and are only `load()`-ed behind a `ClassDB.class_exists("Logger")` gate. On < 4.5 they emit a benign `Could not find base class "Logger"` parse error during the editor's filesystem scan (never instantiated, plugin still works). Don't add a third 4.5+-base script without the same gating + comment.
9296

9397
## Self-update compatibility
9498

@@ -143,7 +147,7 @@ The MCP tool surface is shaped to satisfy two pressures at once:
143147
1. **Anthropic tool-search clients** (`tool_search_tool_bm25_20251119` / `tool_search_tool_regex_20251119`) — non-core tools are tagged `meta={"defer_loading": True}` so the client only loads schemas it searches for.
144148
2. **Tool-count caps in non-search clients** (Antigravity, etc., that ignore `defer_loading` and refuse to start past ~40 tools) — long-tail verbs collapse into per-domain `<domain>_manage` rollups (`op="<verb>"` + `params` dict). Schema-aware clients still see every op via the dynamic `Literal[...]` enum built by `register_manage_tool` in `tools/_meta_tool.py`.
145149

146-
Result: ~39 MCP tools (4 core + ~15 named verbs + ~20 rollups), down from a flat surface that crossed 100. Plugin command names over WebSocket stay independent — they're documented in `tool_catalog.gd` and unchanged by the rollup refactor.
150+
Result: ~40 MCP tools (4 core + 15 named verbs + 21 rollups), down from a flat surface that crossed 100. Plugin command names over WebSocket stay independent — they're documented in `tool_catalog.gd` and unchanged by the rollup refactor.
147151

148152
- All tools follow `domain_action` namespacing — no ambiguous prefixes
149153
- Core tools loaded upfront (no `meta=`): `editor_state`, `scene_get_hierarchy`, `node_get_properties`, `session_activate`
@@ -155,7 +159,7 @@ For tool-capped clients without tool-search support, the server accepts `--exclu
155159

156160
When adding a new verb, prefer adding it as an op on the domain's existing `register_manage_tool(...)` call rather than registering a new top-level tool — only the highest-traffic verbs warrant a named tool (see "Adding a new MCP tool" above).
157161

158-
## Current tool inventory (~39 MCP tools)
162+
## Current tool inventory (~40 MCP tools)
159163

160164
`tool_catalog.gd` is the canonical list — `tests/unit/test_tool_domains.py` keeps it in sync with the Python registrations. The shape:
161165

@@ -181,6 +185,7 @@ When adding a new verb, prefer adding it as an op on the domain's existing `regi
181185
| `signal_manage` | `list`, `connect`, `disconnect` |
182186
| `autoload_manage` | `list`, `add`, `remove` |
183187
| `input_map_manage` | `list`, `add_action`, `remove_action`, `bind_event` |
188+
| `game_manage` | `get_scene_tree`, `get_node_info`, `input_key`, `input_mouse`, `input_gamepad`, `input_state` (running-game inspection + synthetic input; routed to the game process via `game_eval` / `game_command`) |
184189
| `test_manage` | `results_get` |
185190
| `ui_manage` | `set_anchor_preset`, `set_text`, `build_layout`, `draw_recipe` |
186191
| `theme_manage` | `create`, `set_color`, `set_constant`, `set_font_size`, `set_stylebox_flat`, `apply` |

CLAUDE.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -432,14 +432,23 @@ This makes Godot treat the result as a real scene instance: the root shows the f
432432

433433
New features don't ship without tests. Regressions are caught before they merge.
434434

435+
## Godot version support (4.3+)
436+
437+
The plugin supports **Godot 4.3+** (4.4+ recommended). 4.3 is exercised by a CI canary (`Godot tests / Linux (Godot 4.3)` in `ci.yml` — Phase 1 of #477) so the parse-cascade class of regression can't recur unnoticed.
438+
439+
- **Forward-compat engine APIs go through `Engine.call(...)` / `OS.call(...)`, not direct references.** A direct call to a method that only exists in a newer Godot (e.g. `Engine.capture_script_backtraces()`, added in 4.4) is type-checked by the older engine's GDScript **parser** against the native class and rejected at parse time — even when guarded by `Engine.has_method(...)` at runtime. That parse failure cascades (`dispatcher.gd``plugin.gd` preload → `_enter_tree` crash) and bricks the whole plugin on the older engine. This was the #476 regression. The fix: `Engine.call("capture_script_backtraces", false)` keeps identical runtime behavior on 4.4+ while hiding the missing method from the older parser. Apply this pattern to any new newer-than-4.3 engine API.
440+
- **`extends Logger` parses-but-is-unused on < 4.5.** `runtime/editor_logger.gd` and `runtime/game_logger.gd` extend `Logger` (a 4.5+ class). They're only `load()`-ed at runtime behind a `ClassDB.class_exists("Logger")` gate, so they never instantiate on older engines — but Godot's editor filesystem scan still **parses** them and emits a benign `Parse Error: Could not find base class "Logger"` (+ a "Failed to load script" follow-up) per file on any cold scan. Harmless; the plugin loads and works. The header comments in both files document this. `ci-check-gdscript` filters these two signatures only when `ALLOW_LOGGER_PARSE_ERRORS=1` (set on the 4.3 canary row); the 4.6 rows stay strict so a real Logger error on an engine that *has* the class still fails CI.
441+
- **Self-update is broken on 4.3 (#475, open).** The dock's one-click Update *installs* (extracts the new files, shows the green "Updated! Restart the editor." banner), but the extract-over-live-scripts collides with 4.3's stricter `GDScript::reload()` (`!p_keep_state && has_instances → ERR_ALREADY_IN_USE`): a flood of reload errors during install, then a SIGSEGV in `EditorDockManager::remove_dock` / `SceneTree::finalize` on the restart/quit. Approach 1 (skip `remove_control_from_docks` in `_exit_tree`) was disproven — the crash just moves to `SceneTree::finalize`. The only clean 4.3 update path today is **manual**: close the editor, replace `addons/godot_ai/`, relaunch. Do NOT leave a backup copy of the addon *inside* `res://` — Godot scans it and every `class_name` collides ("hides a global script class"). A future fix needs either pre-emptive dock teardown + reload-flood suppression, or gating the Update button off on < 4.5.
442+
435443
## Known issues
436444

437445
- **Re-entrant `_process()` during save**: `EditorInterface.save_scene()` internally renders a preview thumbnail, which triggers frame processing. If `McpConnection._process()` runs during this, WebSocket polling and command dispatch re-enter, crashing Godot (`SIGABRT` in `_save_scene_with_preview`). Fixed by setting `McpConnection.pause_processing = true` around save calls in `SceneHandler`. Any new handler that calls `save_scene()`, `save_scene_as()`, `save_all_scenes()`, or `play_main_scene()` / `play_current_scene()` / `play_custom_scene()` (which internally call `try_autosave()``_save_scene_with_preview`) must do the same. `ProjectHandler.run_project` is the reference for the play path.
438446
- **GDScript tests must not call `EditorInterface.save_scene()` or `scene_create`/`scene_open`**: These trigger modal dialogs or scene switches that freeze or crash the test runner. Test only validation/error paths for these operations in GDScript; full behavior is covered by Python integration tests.
439447
- **GDScript tests must not call `quit_editor` or `reload_plugin`**: These terminate or restart the plugin, killing the test runner. Tested via Python integration tests and CI smoke scripts (`script/ci-quit-test`, `script/ci-reload-test`). (Note: plugin command names stay `quit_editor` / `reload_plugin`; the MCP tool names are `editor_quit` / `editor_reload_plugin`.)
440448
- **Resilient test discovery**: `_discover_suites()` in `test_handler.gd` catches per-file load errors and returns `{suites, errors}`. Individual broken test scripts do not prevent the rest from running. The `errors` list reports which scripts failed to load.
441-
- **CI GDScript validation**: `script/ci-check-gdscript` runs before Godot tests in CI. It scans the `--import` log for `SCRIPT ERROR` / `Parse Error` lines and fails the build early if any GDScript has syntax errors, before the test runner even starts.
442-
- **CI Linux runner**: Linux Godot CI uses `chickensoft-games/setup-godot@v2` on `ubuntu-latest` (not a Docker image). All three OS jobs (Linux, macOS, Windows) use the same chickensoft action for consistent Godot setup. Step timeouts are set on test and smoke steps to prevent CI hangs.
449+
- **CI GDScript validation**: `script/ci-check-gdscript` runs before Godot tests in CI. It scans the `--import` log for `SCRIPT ERROR` / `Parse Error` lines and fails the build early if any GDScript has syntax errors, before the test runner even starts. The benign `extends Logger` parse errors on Godot < 4.5 are suppressed only when `ALLOW_LOGGER_PARSE_ERRORS=1` (set on the 4.3 canary row); default is strict.
450+
- **CI Linux runner**: Linux Godot CI uses `chickensoft-games/setup-godot@v2` on `ubuntu-latest` (not a Docker image). All three OS jobs (Linux, macOS, Windows) use the same chickensoft action for consistent Godot setup, pinned to `4.6.2`. A fourth `godot-tests` row runs the Linux 4.3 canary on `4.3.0` (the action wants 3-part semver — `4.3` / `4.3.stable` are rejected). Step timeouts are set on test and smoke steps to prevent CI hangs.
451+
- **4.3 canary test skips**: four tests exercise 4.4+-only behavior (3 `cli_exec` `OS.execute_with_pipe` capture differences, 1 `dispatcher` `get_stack()` backtrace-format difference) and are skipped on older engines via `McpTestSuite.skip_on_godot_lt("4.4", reason)` (returns `bool` so the test body can early-`return`). The `ci-reload-test` post-churn `test_run` step is skipped on 4.3 via `SKIP_POSTCHURN_TEST_RUN=1` (slow 4.3 GDScript exec outruns the 30s curl timeout; the 10 reload iterations themselves still run).
443452
- **Sleep before test_run in CI**: `script/ci-godot-tests` includes a short sleep (8s) after Godot startup to let the editor filesystem scan settle before running tests. Without this, test discovery can miss files.
444453

445454
## What NOT to do

docs/implementation-plan.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Adjacent reference docs:
4646
- [x] `performance.get_monitors` with optional filter (30 Godot Performance monitors)
4747
- [x] `logs.clear`
4848
- [x] WebSocket buffer increase (4 MB) for large payloads like screenshot base64
49+
- [x] `game_manage` — running-game inspection + synthetic input (#470): `get_scene_tree`, `get_node_info`, `input_key`, `input_mouse`, `input_gamepad`, `input_state`. Routed to the live game process via `game_eval` / `game_command` through the `_mcp_game_helper` autoload, so the AI can read the actual runtime scene tree and drive input while the game plays — not just the editor-side scene.
4950

5051
**Why this matters:** Without a reliable launch-observe-inspect loop, the AI can build project structure but cannot tighten feel, readability, or performance.
5152

@@ -87,9 +88,9 @@ See [Packaging & Distribution](packaging-distribution.md) for full detail. The s
8788
- [x] PyPI / `uvx` path works reliably — automated via `bump-and-release.yml`; live on PyPI as `godot-ai`; `uvx --from godot-ai~=VERSION godot-ai` is the canonical user-install command. Stdio-only clients (Claude Desktop, Zed) bridge through `uvx mcp-proxy`. Stale-index retries (`--refresh`) and cache priming on self-update prevent flaky first-run failures.
8889
- [ ] desktop binary path is real, not aspirational
8990
- [x] plugin is downloadable from the Godot AssetLib — live as [asset/5050](https://godotengine.org/asset-library/asset/5050) and on the new [Godot Asset Store](https://store.godotengine.org/asset/dlight/godot-ai/); release ZIP workflow ships `godot-ai-plugin.zip` via GitHub Releases; dock self-update banner offers one-click upgrades that survive without an editor restart (`update_reload_runner.gd` handoff). Local self-update smoke (`script/local-self-update-smoke`) is the regression gate.
90-
- [x] CI covers Python tests, Godot-side tests, and release-smoke install paths (3 OS × 2 Python + 3 OS Godot + release-smoke). Linux CI uses `chickensoft-games/setup-godot` on `ubuntu-latest`. GDScript parse validation (`ci-check-gdscript`) runs before tests. Step timeouts prevent hangs.
91+
- [x] CI covers Python tests, Godot-side tests, and release-smoke install paths (3 OS × 2 Python + 3 OS Godot @ 4.6.2 + a Linux Godot 4.3 canary + release-smoke). Linux CI uses `chickensoft-games/setup-godot` on `ubuntu-latest`. GDScript parse validation (`ci-check-gdscript`) runs before tests. Step timeouts prevent hangs. The 4.3 canary (#478, Phase 1 of #477) catches the parse-cascade class of regression on the documented-minimum engine; 4.4+-only tests skip via `McpTestSuite.skip_on_godot_lt(...)`.
9192
- [x] bump-and-release workflow — `gh workflow run bump-and-release.yml -f bump=patch/minor/major` bumps versions, commits, tags, and triggers release build
92-
- [ ] compatibility guidance is published and maintained
93+
- [ ] compatibility guidance is published and maintained — README advertises "Godot 4.3+"; 4.3 plugin-load was restored in #476 and is now CI-guarded (4.3 canary). Open gap: in-editor self-update is broken on 4.3 (#475) — manual addon-folder swap is the only clean 4.3 update path; a Update-button gate on < 4.5 is the likely fix.
9394
- [ ] a new user can get from zero to working in under 10 minutes
9495

9596
Release is not just packaging. It is install flow, docs, smoke coverage, and support burden reduction.

docs/testing-strategy.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,14 @@ If a tool has undo semantics, readiness constraints, or cross-session behavior,
128128
The CI stack should exercise at least four tiers:
129129

130130
- Python unit and integration tests (3 OS x 2 Python versions)
131-
- Godot-side editor test suites (3 OS via `chickensoft-games/setup-godot@v2` on GitHub Actions runners) — **headless**; no rendering
131+
- Godot-side editor test suites (3 OS @ Godot 4.6.2 + a Linux Godot 4.3 canary, via `chickensoft-games/setup-godot@v2` on GitHub Actions runners) — **headless**; no rendering. The 4.3 canary (Phase 1 of #477) guards the documented-minimum engine against the parse-cascade class of regression (#476). Tests that depend on 4.4+-only engine behavior skip via `McpTestSuite.skip_on_godot_lt("4.4", reason)`.
132132
- release-surface smoke, especially install and packaging paths once distribution work is active (3 OS)
133133
- local interactive self-update smoke for update/reload/extract changes (`script/local-self-update-smoke`)
134134
- **pixel-level capture smoke** for tools that cross the editor → game-process boundary (3 OS). The `game-capture-smoke-{linux,macos,windows}` jobs launch Godot with a real rendering driver (`xvfb-run -a ... godot --rendering-driver opengl3` on Linux, windowed on macOS and Windows), play `test_project/capture_smoke.tscn` (four colored quadrants), round-trip `editor_screenshot(source="game")` through the debugger-channel bridge, decode the returned PNG with Pillow, and assert the centre of each quadrant matches the expected color within tolerance. Catches regressions in the `_mcp_game_helper` autoload registration, the `DEFERRED_RESPONSE` dispatcher path, and the `McpConnection.send_deferred_response` reply pipeline — none of which are exercised by the headless Godot test suite.
135135

136136
### CI hardening measures
137137

138-
- **GDScript validation**: `script/ci-check-gdscript` runs after `--import` and before the editor launches. It scans the import log for `SCRIPT ERROR` / `Parse Error` lines and fails the build immediately if any GDScript file has syntax errors. This catches broken scripts before the test runner starts.
138+
- **GDScript validation**: `script/ci-check-gdscript` runs after `--import` and before the editor launches. It scans the import log for `SCRIPT ERROR` / `Parse Error` lines and fails the build immediately if any GDScript file has syntax errors. This catches broken scripts before the test runner starts. On the Godot 4.3 canary it sets `ALLOW_LOGGER_PARSE_ERRORS=1` to suppress the two benign `extends Logger` parse errors (the `Logger` base class only exists on 4.5+); the 4.6.2 rows stay strict so a real Logger error on an engine that has the class still fails CI.
139139
- **Step timeouts**: test and smoke steps have `timeout-minutes` set to prevent CI hangs from frozen Godot processes.
140140
- **Filesystem scan settling**: `script/ci-godot-tests` includes a short sleep after editor startup so the filesystem scan completes and test discovery finds all suites.
141141
- **Resilient test discovery**: `test_handler.gd` catches per-file load errors during `_discover_suites()`. A broken test file does not prevent the rest of the suite from running; errors are reported in the response alongside successful results.

0 commit comments

Comments
 (0)