Fix read-only audit findings (correctness, fragility, dead code, tests, docs)#494
Conversation
A read-only audit surfaced 28 actionable findings; this commit fixes them.
Correctness:
- theme set_constant/set_font_size reject non-numeric values instead of
silently coercing garbage to 0 (validate before int(); int("abc")==0 never
tripped the null guard).
- signal_handler type-checks path/signal/target/method before calling
.is_empty(), so a non-string param returns WRONG_TYPE instead of crashing
the dispatcher as an opaque "malformed result".
- script find_symbols now detects `static func` declarations.
- server_lifecycle.stop_server no longer re-appends an already-tracked PID
(produced duplicate kill candidates).
Fragility:
- material apply_to_node falls back to the in-memory material when the
post-save reload returns null (was clearing the slot / crashing get_class()).
- project/session godot:// resources route through safe_payload(_sync) like
every other resource module.
- ci-game-capture-smoke cleanup calls project_manage(op=stop); project_stop is
not a named tool, so the game subprocess was never stopped.
- serve-this-worktree guards `--port` with no following value under set -u.
- test_cli_reload no longer leaks GODOT_AI_DEV_* env vars past teardown.
Efficiency:
- client_configurator check_uv_version / _find_system_install use the bounded
McpCliExec instead of blocking OS.execute on the main thread.
Dead code:
- remove unused _resolve_or_create_player and _snapshot_curve, drop the no-op
particle alias identity-map, and prefix unused scene_root locals with _.
Test quality:
- fix a vacuous readiness-gate assertion (asserted a command name never sent).
- assert returned values, not just key presence, in logs/resource read tests.
- assert color components in test_set_param_color_dict.
- convert a bare return to skip() in test_project.
- rename misnamed integration "pagination" tests to round-trip (real
pagination is covered in test_mcp_tools.py and unit test_pagination.py).
Docs/comments:
- correct stale telemetry milestone/emit-point comment and mark the reserved
record/milestone types; fix plugin.gd logger-build comment, batch history
docstring, and the tools core-tool docstring; rename debugger _session_id
(it is read in the stale-session guard).
Repo hygiene:
- remove orphaned test_telemetry_setting.gd.uid.
Verified: ruff check clean; full pytest (1096 passed, 4 skipped); GDScript
suite 0 failures on Godot 4.6.3 (1366 passed) and 4.3 (1332 passed); 0 parse
errors on both engines.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Routing check_uv_version / _find_system_install through McpCliExec uses OS.execute_with_pipe, whose output capture behaves differently on Godot 4.3 (the documented reason the cli_exec capture tests are skipped on < 4.4). On the 4.3 CI canary, `which godot-ai` came back with empty captured output, so _find_system_install returned "", get_server_command() resolved to nothing, and test_uvx_server_command_uses_exact_pin_not_tilde hit the zero-assertion guard (get_server_command() was empty, so its assert loop never ran). 4.6 captured fine, which is why only the 4.3 canary failed. Finding #17 was a low-severity defensive tweak (bound a hung uv/which); not worth breaking the minimum-supported engine. Restore the original blocking OS.execute, which captures reliably on all versions. client_configurator.gd is now byte-identical to its pre-audit baseline. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Follow-up (commit d092594): reverted finding #17. The first push failed the Finding #17 was a low-severity defensive tweak, so I reverted it rather than break the minimum-supported engine; |
There was a problem hiding this comment.
Pull request overview
This PR addresses a broad set of audit findings across the Godot plugin, Python MCP server/resources, scripts, tests, and documentation comments. It mainly improves validation, resource error handling, cleanup reliability, and removes unused code.
Changes:
- Tightens validation/error handling for theme values, signal params, resource wrappers, material reload fallback, and server PID cleanup.
- Removes unused helpers/aliases and updates stale comments/docstrings.
- Strengthens or renames tests and fixes script/test cleanup behavior.
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
tests/unit/test_runtime_handlers.py |
Tightens assertions for logs and audio readiness gate. |
tests/unit/test_cli_reload.py |
Prevents dev reload env-var leakage across tests. |
tests/integration/test_pagination.py |
Reframes tests as full-result round-trip coverage. |
tests/integration/test_mcp_tools.py |
Tightens logs resource assertion. |
test_project/tests/test_telemetry_setting.gd.uid |
Removes orphaned UID file. |
test_project/tests/test_project.gd |
Marks unavailable editor setting path as skipped. |
test_project/tests/test_material.gd |
Adds color component assertions. |
src/godot_ai/tools/__init__.py |
Corrects core tool documentation. |
src/godot_ai/telemetry.py |
Corrects reserved telemetry/milestone comments. |
src/godot_ai/resources/sessions.py |
Wraps sync session resource in safe payload handling. |
src/godot_ai/resources/project.py |
Wraps project resources in safe payload handling. |
src/godot_ai/resources/__init__.py |
Adds safe_payload_sync. |
script/serve-this-worktree |
Handles missing --port value safely. |
script/ci-game-capture-smoke |
Uses project_manage(op="stop") for cleanup. |
plugin/addons/godot_ai/utils/server_lifecycle.gd |
Avoids duplicate PID kill candidates. |
plugin/addons/godot_ai/plugin.gd |
Clarifies logger loading comment. |
plugin/addons/godot_ai/handlers/theme_handler.gd |
Validates numeric theme scalar inputs before coercion. |
plugin/addons/godot_ai/handlers/texture_handler.gd |
Marks unused scene root local. |
plugin/addons/godot_ai/handlers/signal_handler.gd |
Type-checks signal params before string operations. |
plugin/addons/godot_ai/handlers/script_handler.gd |
Handles static func symbol detection and unused locals. |
plugin/addons/godot_ai/handlers/resource_handler.gd |
Marks unused scene root locals. |
plugin/addons/godot_ai/handlers/particle_handler.gd |
Removes no-op CPU particle alias map. |
plugin/addons/godot_ai/handlers/material_handler.gd |
Falls back to in-memory material on reload failure and marks unused locals. |
plugin/addons/godot_ai/handlers/environment_handler.gd |
Marks unused scene root local. |
plugin/addons/godot_ai/handlers/curve_handler.gd |
Removes unused curve snapshot helper. |
plugin/addons/godot_ai/handlers/batch_handler.gd |
Corrects rollback history comment. |
plugin/addons/godot_ai/handlers/animation_handler.gd |
Removes unused player auto-create helper. |
plugin/addons/godot_ai/debugger/mcp_debugger_plugin.gd |
Renames used debugger session parameter. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ## "stop" has no named tool — it lives only under the project_manage | ||
| ## rollup, so calling "project_stop" returns an unknown-tool error | ||
| ## and the game subprocess is never stopped. | ||
| _tool_call(session_id, "project_manage", {"op": "stop"}, 999, timeout=10) |
| func(theme, name, cls): theme.clear_constant(name, cls), | ||
| func(theme, name, cls): return theme.has_constant(name, cls), | ||
| func(v): return int(v)) | ||
| func(v): return int(v) if (v is int or v is float or (v is String and v.is_valid_int())) else null) |
| func(theme, name, cls): theme.clear_font_size(name, cls), | ||
| func(theme, name, cls): return theme.has_font_size(name, cls), | ||
| func(v): return int(v)) | ||
| func(v): return int(v) if (v is int or v is float or (v is String and v.is_valid_int())) else null) |
| var func_line := line.substr(7).strip_edges() if line.begins_with("static func ") else line | ||
| if func_line.begins_with("func "): | ||
| var func_text := func_line.substr(5).strip_edges() |
- theme set_constant/set_font_size invalid-value errors now append an
"expected an integer" hint instead of _COLOR_HINT ("hex #rrggbb…"), which
was misleading for numeric slots (Copilot finding).
- Add a `static func` to the find_symbols test fixture and assert its name is
detected, locking in the static-func parsing path so it can't silently
regress (Copilot finding).
0 GDScript parse errors on Godot 4.6.3 and 4.3.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Summary
A read-only, adversarially-verified audit of the codebase surfaced 29 findings. This PR fixes 28 of them (one declined — see below). No behavior-defining conventions changed; these are bug fixes, robustness improvements, dead-code removal, comment corrections, and test-assertion tightening.
Correctness
set_constant/set_font_sizenow reject non-numeric values instead of silently coercing garbage to0(int("abc")==0never tripped the existing null guard).path/signal/target/methodbefore.is_empty(), so a non-string param returnsWRONG_TYPEinstead of crashing the dispatcher as an opaque "malformed result".find_symbolsnow detectsstatic funcdeclarations.stop_serverno longer re-appends an already-tracked PID (duplicate kill candidates).Fragility
apply_to_nodefalls back to the in-memory material when the post-save reload returnsnull(was clearing the slot / crashingget_class()).godot://resources route throughsafe_payload(_sync)like every other resource module.project_manage(op="stop")—project_stopis not a named tool, so the game subprocess was never stopped.--portwith no value underset -u.GODOT_AI_DEV_*env vars past teardown.Efficiency
client_configuratorWithdrawn —check_uv_version/_find_system_installuse boundedMcpCliExecinstead of blockingOS.execute.McpCliExecrelies onOS.execute_with_pipe, whose capture is unreliable on Godot 4.3 (broke the 4.3 canary). Reverted;client_configurator.gdis unchanged in this PR. (Net: 2 findings declined — this and the_cli_execpipe-drain.)Dead code
_resolve_or_create_playerand_snapshot_curve, drop the no-op particle alias identity-map, prefix unusedscene_rootlocals with_.Test quality
test_set_param_color_dict.returntoskip()intest_project.test_mcp_tools.py+ unittest_pagination.py).Docs / comments
plugin.gdlogger-build comment, batch history docstring, tools core-tool docstring; rename debugger_session_id(it is read in the stale-session guard).Repo hygiene
test_telemetry_setting.gd.uid.Declined (1)
_cli_exec.gd"drain pipes during the poll loop" — the suggested fix is unsafe:_drain_pipeusesFileAccess.get_as_text(), which reads to EOF and would block until the process exits, defeating the timeout. The bounded-output assumption is already documented at lines 18–20, so the code is left as-is.Verification
ruff check src/ tests/— cleanpytest— 1096 passed, 4 skippedtest_run— 0 failures on Godot 4.6.3 (1366 passed) and 0 failures on Godot 4.3 (1332 passed); 0 parse errors on both engines🤖 Generated with Claude Code