# Sprint 10b Research: Script Error Detection + Real Gameplay Testing

## Codebase Architecture Summary

### Current Error Flow
```
Game Process (runtime.gd) → EngineDebugger("godotiq:error") → debugger.gd → server._record_error()
→ _recent_errors circular buffer (max 10) → _editor_state.recent_errors (last 3 per response)
```

Only **runtime errors** (push_error, crashes during execution) are captured. **Compilation/parse errors** are NOT detected.

### Key Files
- `godotiq_server.gd` (1573 lines): 20+ handlers dispatched via `_dispatch()`. All responses wrapped with `_editor_state` via `send_response()` (line 1529).
- `godotiq_debugger.gd` (104 lines): EditorDebuggerPlugin capturing `godotiq:*` messages from game process.
- `godotiq_runtime.gd`: Game-side autoload handling screenshot, input, exec, state_inspect, etc.
- `bridge/run.py` (30 lines): Simple wrapper — calls `bridge.request("run"/"stop")`.
- `bridge/__init__.py` (776 lines): All MCP tools with error wrapping pattern.

### Handler Registration Pattern
```gdscript
func _dispatch(peer_id, id, method, params):
    match method:
        "ping": _handle_ping(peer_id, id, params)
        "run": _handle_run(peer_id, id, params)
        # ... etc
```

### _editor_state (returned on EVERY response)
```gdscript
func _get_editor_state() -> Dictionary:
    return {
        "open_scene": scene_root.scene_file_path,
        "game_running": EditorInterface.is_playing_scene(),
        "recent_errors": _recent_errors.slice(-3),
    }
```

### Testing Setup
- pytest + pytest-asyncio (`asyncio_mode = "auto"`)
- Bridge tools mocked via `AsyncMock` + `patch("godotiq.tools.bridge.X.get_bridge")`
- Fixtures in `tests/conftest.py`

---

## Web Research: GDScript Error Detection APIs

### Method 1: `load()` + `can_instantiate()` (RECOMMENDED)

`load("res://script.gd")` returns a GDScript object **even if the script has parse errors** (does NOT return null). However:
- `loaded.can_instantiate()` → returns `false` for scripts with parse errors
- `loaded.reload()` → returns non-zero error code (e.g., 43) for scripts with errors

**This is the most reliable in-editor approach.**

Source: [GitHub Issue #96065](https://github.com/godotengine/godot/issues/96065)

### Method 2: `GDScript.new()` + `source_code` + `reload()`

```gdscript
var script = GDScript.new()
script.set_source_code(source_text)
var error = script.reload()  # Returns OK or error code
```

**Caveat**: Can crash editor in debug mode. Works in release mode. ([Issue #109677](https://github.com/godotengine/godot/issues/109677))

### Method 3: No EditorInterface/ScriptEditor Error API

There is **no public API** on `EditorInterface` or `ScriptEditor` to get error lists. `get_script_editor()` provides access to open scripts and editors, but no error/warning information. A [forum thread](https://forum.godotengine.org/t/how-to-get-errors-within-open-script/88328) confirms this gap.

### Method 4: Godot 4.5+ Logger Class

Godot 4.5 introduced `Logger` class for intercepting ALL internal errors:
```gdscript
class_name ErrorLogger extends Logger
static func _static_init():
    OS.add_logger(ErrorLogger.new())

func _log_error(function, file, line, code, rationale, editor_notify, error_type, script_backtraces):
    # Intercepts push_error(), push_warning(), and engine errors
    # error_type includes ERR_ERROR, ERR_WARNING, ERR_SCRIPT
    pass
```

**Caveat**: Virtual functions called from ANY thread — needs Mutex. Don't call print/push_error inside handlers (infinite recursion).

Source: [Logger API docs](https://docs.godotengine.org/en/4.5/classes/class_logger.html)

---

## Web Research: Log File Locations

### Per-Platform Paths

| Platform | user:// path |
|----------|-------------|
| macOS | `~/Library/Application Support/Godot/app_userdata/<PROJECT_NAME>/` |
| Linux | `~/.local/share/godot/app_userdata/<PROJECT_NAME>/` |
| Windows | `%APPDATA%\Godot\app_userdata\<PROJECT_NAME>\` |

Log files at `user://logs/godot.log` (or `user://logs/log.txt` depending on config).

### Accessing from GDScript
- `OS.get_user_data_dir()` → absolute filesystem path for `user://`
- `ProjectSettings.globalize_path("user://logs/godot.log")` → full path

### Log Format
```
SCRIPT ERROR: Parse Error: Expected closing ")" after call arguments.
   at: res://main.gd:3
ERROR: Cannot get class ''.
   at: _instantiate_internal (core/object/class_db.cpp:546)
```

### Requirement
File logging must be enabled: `debug/file_logging/enable_file_logging = true` in ProjectSettings.

---

## Web Research: CLI Validation Flags

### `--check-only` Flag
```bash
godot --headless --check-only --script res://path/to/script.gd
```

**Limitations:**
- **Single script only** — no `--check-all` or `--validate-project` flag
- **Autoload false positives** — scripts referencing singletons get false errors ([PR #110295](https://github.com/godotengine/godot/pull/110295) open, not merged)
- `--debug` flag needed for warnings but may crash on macOS

### No Batch Checking
[Proposal #1758](https://github.com/godotengine/godot-proposals/issues/1758) and [Issue #20513](https://github.com/godotengine/godot/issues/20513) request this but remain open.

---

## Recommended Approach for Error Detection

Based on research, the **best approach** for GodotIQ:

### Primary: `load()` + `can_instantiate()` in-editor

New handler `_handle_check_errors` that:
1. Scans all `.gd` files in `res://` using `EditorInterface.get_resource_filesystem()`
2. For each: `load(path)` → check `can_instantiate()` and `reload()` return
3. Returns array of `{file, error}` dicts

**Pros**: Works reliably, no crash risk, runs inside editor context.
**Cons**: `load()` caches — need `ResourceLoader.load(path, "", ResourceLoader.CACHE_MODE_IGNORE)` to avoid stale cache.

### Secondary: Integrate into `_editor_state`

Add a `script_errors` field to `_get_editor_state()` that runs a lightweight check on recently modified scripts (not all scripts — too expensive for every response).

### Fallback: Log file parsing

If in-editor approach fails, parse `user://logs/godot.log` for `SCRIPT ERROR` lines.

### Future: Logger class (Godot 4.5+)

When targeting Godot 4.5+, use the `Logger` class to intercept script errors in real-time and store them in `_recent_errors`.
