You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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>
-`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`)
18
18
-`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)
78
78
79
79
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.
80
80
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
+
81
83
## GDScript conventions
82
84
83
85
- 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
89
91
- Use `McpScenePath.from_node()` / `McpScenePath.resolve()` for clean paths like `/Main/Camera3D`
90
92
- Use `##` for doc comments, typed arrays (`Array[String]`), never Python-style `"""`
91
93
- 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.
92
96
93
97
## Self-update compatibility
94
98
@@ -143,7 +147,7 @@ The MCP tool surface is shaped to satisfy two pressures at once:
143
147
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.
144
148
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`.
145
149
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.
147
151
148
152
- All tools follow `domain_action` namespacing — no ambiguous prefixes
@@ -155,7 +159,7 @@ For tool-capped clients without tool-search support, the server accepts `--exclu
155
159
156
160
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).
157
161
158
-
## Current tool inventory (~39 MCP tools)
162
+
## Current tool inventory (~40 MCP tools)
159
163
160
164
`tool_catalog.gd` is the canonical list — `tests/unit/test_tool_domains.py` keeps it in sync with the Python registrations. The shape:
161
165
@@ -181,6 +185,7 @@ When adding a new verb, prefer adding it as an op on the domain's existing `regi
|`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`) |
Copy file name to clipboardExpand all lines: CLAUDE.md
+11-2Lines changed: 11 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -432,14 +432,23 @@ This makes Godot treat the result as a real scene instance: the root shows the f
432
432
433
433
New features don't ship without tests. Regressions are caught before they merge.
434
434
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
+
435
443
## Known issues
436
444
437
445
-**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.
438
446
-**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.
439
447
-**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`.)
440
448
-**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).
443
452
-**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.
Copy file name to clipboardExpand all lines: docs/implementation-plan.md
+3-2Lines changed: 3 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -46,6 +46,7 @@ Adjacent reference docs:
46
46
-[x]`performance.get_monitors` with optional filter (30 Godot Performance monitors)
47
47
-[x]`logs.clear`
48
48
-[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.
49
50
50
51
**Why this matters:** Without a reliable launch-observe-inspect loop, the AI can build project structure but cannot tighten feel, readability, or performance.
51
52
@@ -87,9 +88,9 @@ See [Packaging & Distribution](packaging-distribution.md) for full detail. The s
87
88
-[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.
88
89
-[ ] desktop binary path is real, not aspirational
89
90
-[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(...)`.
91
92
-[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.
93
94
-[ ] a new user can get from zero to working in under 10 minutes
94
95
95
96
Release is not just packaging. It is install flow, docs, smoke coverage, and support burden reduction.
Copy file name to clipboardExpand all lines: docs/testing-strategy.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -128,14 +128,14 @@ If a tool has undo semantics, readiness constraints, or cross-session behavior,
128
128
The CI stack should exercise at least four tiers:
129
129
130
130
- 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)`.
132
132
- release-surface smoke, especially install and packaging paths once distribution work is active (3 OS)
133
133
- local interactive self-update smoke for update/reload/extract changes (`script/local-self-update-smoke`)
134
134
-**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.
135
135
136
136
### CI hardening measures
137
137
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.
139
139
-**Step timeouts**: test and smoke steps have `timeout-minutes` set to prevent CI hangs from frozen Godot processes.
140
140
-**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.
141
141
-**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