Add screenshot-testing recipe doc + local game-capture diagnostic#414
Merged
Conversation
docs/screenshot-testing.md captures the agent-facing recipe for editor_screenshot — when to pick viewport vs game vs cinematic, the project_run → poll game_capture_ready → screenshot pattern, the pre-flight checklist for the "autoload never registered its debugger capture within 20s" timeout, and a decision tree for diagnosing it. Also calls out preferring state assertions (node_get_properties, game print() forwarded over mcp:log_batch) over pixel screenshots when the test isn't truly visual. script/local-game-capture-diag is the developer-facing companion to ci-game-capture-smoke: works against whatever scene is currently open (no fixture scene required), dumps the autoload list and project.godot [autoload] section before touching the game, polls game_capture_ready deterministically, and on failure dumps recent plugin + game logs so the failure mode is visible (autoload missing vs game crashed during boot vs F5-launched-not-project_run vs wrong session active).
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds guidance and a local diagnostic script to make editor_screenshot(source="game") more reliable and debuggable, especially for the common “game-side autoload never registered…” timeout scenario.
Changes:
- Add an agent-facing documentation recipe for screenshot-driven testing, including readiness polling via
editor_state.game_capture_ready. - Add a developer-facing local diagnostic script to validate the editor↔game screenshot bridge and dump autoload + logs for failure triage.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
script/local-game-capture-diag |
New local diagnostic to connect to MCP, check autoload persistence, poll game_capture_ready, and attempt a game screenshot. |
docs/screenshot-testing.md |
New documentation describing when to use each screenshot source and a recommended run/poll/screenshot/stop workflow. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+342
to
+345
| if we_started_run and not args.keep_running: | ||
| try: | ||
| _tool_call(args.url, sid, "project_stop", {}, 999, timeout=10) | ||
| except Exception as exc: |
Comment on lines
+14
to
+16
| 5. call editor_screenshot(source="game") and print PNG dimensions + | ||
| byte size — does NOT assert on pixel colors, so it works against | ||
| any scene |
Comment on lines
+233
to
+246
| print(f"Connecting to {args.url}") | ||
| try: | ||
| sid = _initialize(args.url) | ||
| sess = _wait_for_session(args.url, sid) | ||
| except (urllib.error.URLError, RuntimeError) as exc: | ||
| print(f"\nSetup failed: {exc}", file=sys.stderr) | ||
| print( | ||
| "Is the Godot editor open with the plugin enabled?\n" | ||
| "Is the MCP server running on the expected port?", | ||
| file=sys.stderr, | ||
| ) | ||
| return 2 | ||
| print(f"Session: {sess.get('active_session_id') or sess}") | ||
|
|
| 2. project_run(mode="current", -> autosave=False so MCP scene mutations stay in memory | ||
| autosave=False) | ||
| 3. poll editor_state every ~500ms -> wait until is_playing=true AND game_capture_ready=true | ||
| 4. (interact: input_send / node_set_property / call_method as needed) |
| 4. (interact: input_send / node_set_property / call_method as needed) | ||
| 5. editor_screenshot(source="game", -> request the capture | ||
| max_resolution=1280) | ||
| 6. project_stop -> always stop the run when done |
Comment on lines
+76
to
+77
| → Did you `project_stop` and forget to `project_run` again? Each new run | ||
| rotates a token; the readiness flag is reset on `begin_game_run()`. |
| parser.add_argument( | ||
| "--keep-running", | ||
| action="store_true", | ||
| help="Skip project_stop on exit (default is to stop the run we started).", |
…ims, --session flag - project_stop is not a top-level MCP tool — stop is project_manage(op="stop"). Updated both doc references (recipe step 6 + decision-tree mention) and the script's cleanup call + --keep-running help text. - input_send / call_method aren't registered tools. Genericized recipe step 4 to point at the actual interaction surface (node_set_property, batch_execute, ui_manage, theme_manage) without prescribing a specific tool. - Module docstring promised "PNG dimensions + byte size" but only printed byte length. Added _png_dimensions(): parses width/height from the IHDR chunk (zero-dep), printed alongside byte count on success. - Added --session <hint> flag for the multi-editor routing case the doc explicitly calls out as a timeout cause. Calls session_activate before any other tool, prints the resolved session, fails fast on no-match.
This was referenced May 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
Surfaced by a user who kept hitting
while having an AI client test a runtime UI option (a new main-menu entry).
The 20s timeout in
mcp_debugger_plugin.gd:202-204fires whenever theeditor sends
mcp:take_screenshotbut never seesmcp:hellofrom thegame-side autoload — and there are several sharp edges that make AI agents
hit it repeatedly:
mcp:hellois only honored when_game_run_active=true, i.e. when thecurrent play cycle was started by
project_run. Manual F5 plays beaconis explicitly ignored (
mcp_debugger_plugin.gd:113-116), so any AI thatreaches for
editor_screenshot(source="game")without first callingproject_runwill time out.editor_statealready exposesgame_capture_ready(set by_setup_session/
begin_game_runand flipped true onmcp:hello), but agents don'talways know to poll it — they sleep instead, miss the window, and time out.
_mcp_game_helperactually persisted toproject.godot?" requires either eyeballing the file or callingautoload_manage(op="list"), which agents skip.a session whose game isn't running.
Changes
docs/screenshot-testing.md— agent-facing recipe:sourceselection table (when to useviewportvsgamevscinematic).project_run(autosave=False)→ polleditor_state.game_capture_ready→editor_screenshot(source="game")→project_stop.node_get_properties,print()from the game forwarded over
mcp:log_batch) when the assertion isn'ttruly visual — screenshots are the slowest, flakiest assertion surface.
script/local-game-capture-diag— developer-facing companion to theexisting
script/ci-game-capture-smoke:capture_smoke.tscnfixture required) so it's usable for "is this broken in MY project?".
autoload_manage) AND the on-disk[autoload]section ofproject.godot(filesystem_manageread_text)before touching the game, so the autoload-missing case is visible.
game_capture_readydeterministically — same readiness signal theCI smoke uses, no sleep-and-pray.
logs_readandlogs_read source="game") so a crashed boot vs. a missing autoload vs.a wrong session is distinguishable.
--run/--no-screenshot/--keep-runningflags for the commondiagnostic shapes.
Test plan
python3 -c "import ast; ast.parse(open('script/local-game-capture-diag').read())"— done locallyruff check script/local-game-capture-diag— done locallyscript/local-game-capture-diag --helprenders argparse correctly — done locallyscript/local-game-capture-diag --runagainst an open editor on a project with_mcp_game_helperregistered → expectPASS: end-to-end game capture bridge works._mcp_game_helperfromproject.godotand rerun → expect the autoload-missing path to print the project.godot section with the entry absent and the actionable HINT.docs/screenshot-testing.mdrendered on GitHub for formatting.No code paths in
src/orplugin/change — this is docs + a diagnosticscript.
🤖 Generated with Claude Code
Generated by Claude Code