# Section 01: Logger + Error Buffer (godotiq_server.gd)

## Overview

This section adds an optional GDScript Logger inner class (Godot 4.5+ only) and a thread-safe `_script_errors` buffer to `godotiq_server.gd`. The Logger intercepts script compilation and parse errors from the engine, storing them in a separate buffer from the existing `_recent_errors` (which captures runtime errors). This is a **foundational component** -- later sections (02, 03) depend on the buffer being in place.

The Logger is an **enhancement, not a requirement**. The core error-checking approach (`load() + can_instantiate()`) works on any Godot 4.x version. When the Logger is available (4.5+), it adds line-number detail and specific error messages to the results.

**No Python tests for this section** -- it is entirely GDScript. Verification is manual.

---

## File to Modify

`/Users/salvatorefarruggio/Desktop/godotiq/godot-addon/addons/godotiq/godotiq_server.gd`

---

## Tests (Manual Verification)

This section has no automated Python tests. The following manual verification steps should be performed once the code is deployed in the Godot editor:

1. **Logger registers without error on Godot 4.5+** -- Open the Godot editor with the addon enabled. No errors in the Output panel related to Logger registration. Confirm the print message `"GodotIQ: Logger registered (Godot 4.5+)"` appears.

2. **Logger is skipped gracefully on Godot < 4.5** -- On a Godot 4.3 or 4.4 editor, the addon loads without errors. No Logger-related messages appear. `_has_logger` is `false`.

3. **Logger captures error when a broken `.gd` script is `reload()`ed** -- Create a script with intentional errors (e.g., `var x: int = "hello"`). When `check_errors` triggers `reload()` on it, the Logger's `_log_error` captures the error entry in `_script_errors`.

4. **`_script_errors` buffer respects max size (50)** -- After flooding the Logger with more than 50 errors, the buffer does not grow beyond 50 entries.

5. **`_clear_script_errors` empties the buffer** -- Call the clear method, then verify `_get_script_errors()` returns an empty array.

6. **`_get_script_errors` returns a copy (not a reference)** -- Modify the returned array; the internal buffer remains unchanged.

---

## Implementation Details

### 1. New Instance Variables

Add the following instance variables to `godotiq_server.gd`, alongside the existing `_recent_errors` and `MAX_RECENT_ERRORS` declarations (around line 28-29 of the current file):

```gdscript
var _script_errors: Array = []  # [{file, line, message, type, timestamp}]
var _script_error_mutex: Mutex = Mutex.new()
const MAX_SCRIPT_ERRORS: int = 50
var _error_logger  # Variant -- null if Logger unavailable
var _has_logger: bool = false
```

Key points:
- `_script_errors` is a **separate buffer** from `_recent_errors`. The existing runtime error buffer is untouched.
- `_error_logger` is declared as an untyped `Variant` (no type hint) because the `Logger` class does not exist on Godot < 4.5 and a typed declaration would cause a parse error.
- `_has_logger` is a simple boolean flag checked by later sections (02, 03) to decide whether to read Logger-captured details.

### 2. Logger Separate File

Since GDScript parses all inner classes at load time, defining a class that extends `Logger` (which doesn't exist in Godot < 4.5) as an inner class would cause parse errors on older versions. Instead, create a **separate file** that is conditionally `load()`ed at runtime.

#### New File: `godot-addon/addons/godotiq/godotiq_logger.gd`

Create a new file at `/Users/salvatorefarruggio/Desktop/godotiq/godot-addon/addons/godotiq/godotiq_logger.gd`.

This file extends `Logger` and implements:

- **Constructor** -- takes a reference to the server (specifically, a `Callable` for appending errors) so it can write to `_script_errors` without holding a direct reference to the server node.
- **`_log_error(function, file, line, code, rationale, editor_notify, error_type, script_backtraces)`** -- the virtual method called by the engine for every error. Filters for script-related errors:
  - `file` ends in `.gd` or `.tscn`
  - OR `error_type` is `ERR_SCRIPT`
  - Constructs a structured entry: `{file: String, line: int, message: String, type: String, timestamp: float}`
  - Calls the append callable to add to the buffer
  - Does **NOT** call `print()` or `push_error()` inside the handler (prevents infinite recursion)

The `_log_error` signature matches Godot 4.5's `Logger._log_error` virtual method.

### 3. Mutex Access Methods

Add three methods to `godotiq_server.gd` for thread-safe access to `_script_errors`. The Logger callback can be called from any thread, so all access must go through the mutex.

Each operation acquires and releases the mutex independently. There is no nested mutex acquisition across these methods -- the Logger callback calls `_append_script_error`, and the handler calls `_clear_script_errors` then later `_get_script_errors`. These never overlap within the same call stack, so deadlock is impossible.

```gdscript
func _clear_script_errors() -> void:
    _script_error_mutex.lock()
    _script_errors.clear()
    _script_error_mutex.unlock()

func _append_script_error(entry: Dictionary) -> void:
    _script_error_mutex.lock()
    if _script_errors.size() < MAX_SCRIPT_ERRORS:
        _script_errors.append(entry)
    _script_error_mutex.unlock()

func _get_script_errors() -> Array:
    _script_error_mutex.lock()
    var copy := _script_errors.duplicate()
    _script_error_mutex.unlock()
    return copy
```

### 4. Conditional Registration in `_ready()`

At the end of the existing `_ready()` function (after the TCP server setup), add the Logger registration:

```gdscript
# In _ready(), after TCP server listen:
if ClassDB.class_exists("Logger"):
    var logger_script = load("res://addons/godotiq/godotiq_logger.gd")
    if logger_script:
        _error_logger = logger_script.new(self)
        OS.add_logger(_error_logger)
        _has_logger = true
        print("GodotIQ: Logger registered (Godot 4.5+)")
```

Key details:
- `ClassDB.class_exists("Logger")` is the version check -- returns `false` on Godot < 4.5.
- `load()` is used instead of `preload()` to avoid parse-time errors if the file references the `Logger` class.
- The Logger constructor receives `self` (the server node) so it can call `_append_script_error()`.
- `OS.add_logger()` registers the Logger with the engine.
- If `load()` fails (e.g., file missing), the guard `if logger_script:` prevents a crash. `_has_logger` stays `false`.

### 5. Cleanup in `_exit_tree()`

In the existing `_exit_tree()` method, add Logger cleanup before the TCP server teardown:

```gdscript
# In _exit_tree(), before TCP cleanup:
if _has_logger and _error_logger:
    OS.remove_logger(_error_logger)
    _error_logger = null
    _has_logger = false
```

---

## Dependencies

- **No dependencies** -- this is Section 01, the foundation layer.
- **Blocks:** Section 02 (check_errors handler reads `_script_errors` and uses `_has_logger`), Section 03 (pre-run check uses `_has_logger` to decide whether to clear the Logger buffer).

## Summary of Changes

| File | Change |
|------|--------|
| `godot-addon/addons/godotiq/godotiq_server.gd` | Add instance variables, mutex methods, Logger registration in `_ready()`, cleanup in `_exit_tree()` |
| `godot-addon/addons/godotiq/godotiq_logger.gd` | **New file** -- Logger subclass extending `Logger`, implements `_log_error` with script-error filtering |

## Implementation Notes (Post-Review)

- Used `ERR_SCRIPT` enum constant instead of magic number `2` in the logger filter
- Changed `_get_script_errors()` to use `.duplicate(true)` (deep copy) so returned dict entries are independent
- Added `push_warning()` when Logger class exists but `godotiq_logger.gd` fails to load
