# GodotIQ v0.1.4 — Complete Specification

## Overview

Fix 15 issues discovered during real-world testing with a dungeon crawler demo game. Each fix must include new tests, and all existing tests must continue to pass. Implementation order: impact-first (build_scene → node_ops → exec → screenshot → input → smaller fixes → packaging).

---

## Issue 1: BUILD_SCENE Grid Offset

**Tool:** `godotiq_build_scene` (Python: `src/godotiq/tools/bridge/build_scene.py`, GDScript: `godotiq_server.gd`)

**Problem:** Grid always starts at world origin. No way to offset placed nodes.

**Solution:** Add `"offset": [x, y, z]` to the build config. Default `[0, 0, 0]`.

**Scope:** ALL placement modes (grid, scatter, line) — per interview Q1.

**Details:**
- Grid mode: Add offset to each node's computed position. `[ox + col*spacing + offset_x, oy + offset_y, oz + row*spacing + offset_z]`
- Scatter mode: Add offset to each item's position
- Line mode: Add offset to each computed point position
- Overrides with custom `position` keys should ALSO have offset applied
- Validation: offset must be array of 3 numbers if provided, default `[0,0,0]`

**Files to modify:**
- `src/godotiq/tools/bridge/build_scene.py` — validate offset parameter
- `godot-addon/addons/godotiq/godotiq_server.gd` — apply offset in `_expand_build_grid`, `_expand_build_line`, scatter handling

---

## Issue 2: BUILD_SCENE Unique Names

**Tool:** `godotiq_build_scene`

**Problem:** Nodes are named "BuildNode" or generic "Node" prefix.

**Solution:** Default naming based on scene filename:
- Grid: `{scene_filename}_{row}_{col}` (e.g. `floor_0_0`)
- Scatter: `{scene_filename}_{index}` (e.g. `wall_0`)
- Line: `{scene_filename}_{index}` (e.g. `torch_2`)
- If item has explicit `"name"` in config, use that instead

**Details:**
- `scene_filename` = filename without extension from the `scene` path (e.g. `res://assets/floor.tscn` → `floor`)
- GDScript side: change prefix logic in `_expand_build_grid`, `_expand_build_line`, scatter expansion
- Scatter items may already have individual `name` fields — those take priority

**Files to modify:**
- `godot-addon/addons/godotiq/godotiq_server.gd` — change prefix derivation in `_expand_build_grid`, `_expand_build_line`, scatter item handling

---

## Issue 3: BUILD_SCENE Parent Path Stripping

**Tool:** `godotiq_build_scene`

**Problem:** If user passes `"DungeonCrawler/Room1"` as parent but root is `"DungeonCrawler"`, it fails with PARENT_NOT_FOUND.

**Solution:**
1. Auto-strip first segment if it matches root node name: `DungeonCrawler/Room1` → `Room1`
2. Improve error message: `"root node is '{name}', use relative paths from root children"`

**Details (per interview Q3):**
- Only strip first segment if it matches root name
- `DungeonCrawler/Room1/Torch` → `Room1/Torch` when root is `DungeonCrawler`
- Apply stripping in `_find_node_by_name_or_path` or before calling it

**Files to modify:**
- `godot-addon/addons/godotiq/godotiq_server.gd` — parent path resolution in `_handle_build_scene`

---

## Issue 4: CHECK_ERRORS False Positive Filtering

**Tool:** `godotiq_check_errors` (Python: `src/godotiq/tools/bridge/check_errors.py`)

**Problem:** Scripts reporting errors at line 0 are almost certainly false positives (cache/reload artifacts).

**Solution:**
- Add `"confidence": "low"` to any error with `line == 0` or no line number
- When line-0 errors are present, add top-level `"note": "line 0 errors are often false positives — try reloading the scene"`
- Errors with line > 0 get `"confidence": "high"`

**Files to modify:**
- `src/godotiq/tools/bridge/check_errors.py` — post-process bridge response to annotate confidence
- Possibly `godotiq_server.gd` — add confidence field at error creation time

---

## Issue 5: EXEC Error Detail Extraction

**Tool:** `godotiq_exec` (Python: `src/godotiq/tools/bridge/exec_code.py`, GDScript: `godotiq_server.gd`)

**Problem:** `"Compilation failed (error 43)"` gives no useful detail.

**Solution (both sides, per interview Q6):**
- GDScript side: Capture GDScript compilation error details (line number, message) when `GDScript.new().source_code` compilation fails. Use the error logging system to extract structured error info.
- Python side: Parse the structured error response into `"error_detail": "line 12: Invalid set index..."` format
- Return `error_detail` alongside existing `error` field

**Files to modify:**
- `godot-addon/addons/godotiq/godotiq_server.gd` — exec handlers to capture compilation details
- `src/godotiq/tools/bridge/exec_code.py` — format error_detail from structured response

---

## Issue 6: EXEC Await Warning

**Tool:** `godotiq_exec`

**Problem:** Users write `await create_timer().timeout` and hit timeout with no explanation.

**Solution:** Add warning to exec tool docstring:
> `WARNING: func run() must return synchronously or within timeout_ms. Do not use long await chains (e.g. await create_timer().timeout) as they will cause timeout.`

**Files to modify:**
- `src/godotiq/tools/bridge/exec_code.py` — update tool docstring

---

## Issue 7: ASSET_REGISTRY Fuzzy Match Suggestions

**Tool:** `godotiq_asset_registry` (Python: `src/godotiq/tools/assets/__init__.py`)

**Problem:** When path_filter returns 0 results, user has no idea what directories exist.

**Solution:** If path_filter matches 0 assets, scan project for directories containing asset files (.glb, .tscn, .tres, .png, etc.) and return them as `"suggestions": ["dungeon_glb/", "fantasy_town_glb/"]`.

**Details:**
- Only trigger when path_filter is provided and yields 0 results
- Collect unique directory names (relative to project root) that contain recognized asset extensions
- Return top 10-20 suggestions sorted alphabetically
- Add to response alongside empty results

**Files to modify:**
- `src/godotiq/tools/assets/__init__.py` — add suggestion logic when filter yields empty results

---

## Issue 8: SCENE_MAP Disk Warning

**Tool:** `godotiq_scene_map` (Python: `src/godotiq/tools/spatial/__init__.py`)

**Problem:** Users don't realize scene_map reads from saved .tscn file, not live editor state.

**Solution:** Add `"note": "reads from saved .tscn file — call godotiq_save_scene first if you have unsaved editor changes"` to the response.

**Files to modify:**
- `src/godotiq/tools/spatial/__init__.py` — add note field to scene_map response

---

## Issue 9: SAVE_SCENE Feedback

**Tool:** `godotiq_save_scene` (Python: `src/godotiq/tools/bridge/save_scene.py`)

**Problem:** Response only returns `"saved": true` with no useful metadata.

**Solution:** Return `"file_size_kb": 245, "node_count": 47` alongside existing response.

**Details:**
- GDScript side: After save, read file size and count scene tree nodes
- Or Python side: After bridge response, read the .tscn file size and parse node count
- Prefer GDScript side since it has direct access to scene tree and filesystem

**Files to modify:**
- `godot-addon/addons/godotiq/godotiq_server.gd` — add file_size_kb and node_count to save_scene response
- `src/godotiq/tools/bridge/save_scene.py` — pass through new fields

---

## Issue 10: SCREENSHOT Crop Region

**Tool:** `godotiq_screenshot` (Python: `src/godotiq/tools/bridge/screenshot.py`)

**Problem:** No way to zoom into specific areas without increasing total response size.

**Solution:** Add optional `region: [x, y, width, height]` parameter (in pixels).

**Behavior (per interview Q4):**
- If region provided, crop captured image to that region before encoding
- Clamp to image bounds if region exceeds dimensions
- Include warning if clamping occurred: `"crop_warning": "region was clamped to image bounds (original: [x,y,w,h], clamped: [x,y,w,h])"`
- Region coordinates are in the SCALED image space (after scale is applied)

**Implementation:**
- GDScript side: After capture and scale, use `Image.get_region(Rect2i(x, y, w, h))` to crop
- Validate: x >= 0, y >= 0, w > 0, h > 0

**Files to modify:**
- `src/godotiq/tools/bridge/screenshot.py` — add region parameter, validation
- `godot-addon/addons/godotiq/godotiq_server.gd` — implement crop via `Image.get_region()`

---

## Issue 11: INPUT Mouse Motion

**Tool:** `godotiq_input` (Python: `src/godotiq/tools/bridge/input_sim.py`)

**Problem:** No way to simulate mouse movement for FPS camera testing.

**Solution:** Add `{"mouse_motion": {"relative_x": 200, "relative_y": 0}}` command type.

**Implementation:**
- GDScript side: Create `InputEventMouseMotion.new()`, set `relative = Vector2(x, y)`, dispatch via `Input.parse_input_event()`
- Optional: `position`, `velocity` fields (can be computed or omitted)
- Python side: Add validation for mouse_motion command type

**Files to modify:**
- `src/godotiq/tools/bridge/input_sim.py` — add mouse_motion command type validation
- `godot-addon/addons/godotiq/godotiq_runtime.gd` or equivalent — handle mouse_motion input event creation

---

## Issue 12: NODE_OPS Rename + Get Property

**Tool:** `godotiq_node_ops` (Python: `src/godotiq/tools/bridge/node_ops.py`)

**Problem:** No rename or property read operations.

**Solution:** Add two new operations:
- `{"op": "rename", "node": "BuildNode", "new_name": "Wall_North_0"}`
- `{"op": "get_property", "node": "Room1/Torch1", "property": "position"}` → `{"value": [2, 0, -0.5]}`

**Type handling for get_property (per interview Q8):** Typed with fallback:
- `Vector2` → `[x, y]`
- `Vector3` → `[x, y, z]`
- `Color` → `"#rrggbbaa"` or `[r, g, b, a]`
- `int`, `float`, `bool`, `String` → native JSON types
- Complex types → string representation

**Files to modify:**
- `src/godotiq/tools/bridge/node_ops.py` — add rename/get_property to allowed ops, validation
- `godot-addon/addons/godotiq/godotiq_server.gd` — implement rename and get_property handlers with type conversion

---

## Issue 13: Plugin.cfg Version Sync

**Problem:** `plugin.cfg` version is hardcoded and doesn't match `pyproject.toml`.

**Solution (per interview Q5):** Pre-build hook reads pyproject.toml version and updates:
1. `godot-addon/addons/godotiq/plugin.cfg` line 6
2. `godot-addon/addons/godotiq/godotiq_server.gd` ADDON_VERSION constant

**Implementation:**
- Create a build script (e.g. `scripts/sync_version.py`) that:
  1. Reads version from pyproject.toml
  2. Updates plugin.cfg `version=` line
  3. Updates godotiq_server.gd `ADDON_VERSION :=` line
- Hook into hatch build via `[tool.hatch.build.hooks]` or a Makefile target
- Version sync runs before `python -m build`

**Files to modify/create:**
- New: `scripts/sync_version.py`
- `pyproject.toml` — add build hook config if possible
- `godot-addon/addons/godotiq/plugin.cfg`
- `godot-addon/addons/godotiq/godotiq_server.gd`

---

## Issue 14: Dev Override Security

**Problem:** `GODOTIQ_LICENSE=pro` is a plain string bypass — insecure.

**Solution:** Replace with SHA-256 hashed dev key:
1. Generate a random secure passphrase
2. Compute SHA-256 hash and hardcode in `license.py`
3. At runtime: `GODOTIQ_DEV_KEY` env var → SHA-256 hash → compare with hardcoded hash
4. Remove the `GODOTIQ_LICENSE=pro` bypass entirely

**Files to modify:**
- `src/godotiq/license.py` — replace line 119 bypass with hashed key check

---

## Issue 15: CLI Version Warning Suppression

**Problem:** `godotiq --version` prints "no project found" warning before version number.

**Solution:** Suppress all warnings when `--version` flag is present. Check for `--version` in `sys.argv` before any code that might emit warnings.

**Files to modify:**
- `src/godotiq/cli.py` — early check for `--version` flag, suppress warnings

---

## Verification

After all fixes:
1. `pytest tests/ -v` → all pass (old + new)
2. Bump version to 0.1.4 in `pyproject.toml`
3. Run version sync script
4. `rm -rf dist/ build/ && .venv/bin/python -m build`
5. Do NOT upload to PyPI
