# Sprint 5f: Spatial Validation — Usage Guide

## Quick Start

Spatial validation is built into `godotiq_node_ops`. Add `validate: true` to any move, scale, or add_child operation to check the target against the scene's spatial layout before executing.

```python
# Basic usage — validate a move operation
godotiq_node_ops(
    operations=[
        {"op": "move", "node": "Shelf_04", "position": [3, 0, 2], "validate": true}
    ]
)

# Specify which scene to validate against (defaults to main scene)
godotiq_node_ops(
    scene="res://scenes/main.tscn",
    operations=[
        {"op": "move", "node": "Shelf_04", "position": [3, 0, 2], "validate": true},
        {"op": "scale", "node": "Shelf_04", "scale": [1, 1, 1], "validate": true},
    ]
)
```

## What Gets Checked

| Check | Severity | Condition |
|-------|----------|-----------|
| Wall proximity | BLOCKED | XZ distance < 0.2m from wall/floor/ceiling |
| Wall proximity | WARNING | XZ distance 0.2-0.5m from wall |
| Node overlap | BLOCKED | 3D distance < 0.05m from same-depth node |
| Sibling outlier | WARNING | Position > 3x mean sibling distance |
| Scale outlier | WARNING | Scale > 3x different from sibling average |
| Invalid input | BLOCKED | Position/scale with < 3 components |

## Severity Levels

- **OK** — No issues. Operation proceeds normally.
- **WARNING** — Suspicious but allowed. Warning attached to response.
- **BLOCKED** — Invalid. Entire batch rejected atomically (no changes made).

## Response Format

When validation runs, the response includes a `validation` array:

```json
{
  "results": [...],
  "validation": [
    {"valid": true, "severity": "WARNING", "message": "Sibling outlier: ...", "checks": [...]}
  ]
}
```

When blocked:

```json
{
  "status": "BLOCKED",
  "validation": [...],
  "message": "One or more operations blocked by spatial validation. No changes made.",
  "operations_blocked": 1
}
```

## API Reference

### `spatial_validator.validate_move(target_position, node_name, scene_data)`

Pure function. Checks a target position against scene spatial data.

- `target_position`: `list[float]` — 3-element [x, y, z]
- `node_name`: `str` — Name of the node being moved
- `scene_data`: `dict` — `{"nodes": [{"name", "type", "position", "scale", "depth"}]}`
- Returns: `dict` with `valid`, `severity`, `message`, `suggestion`, `checks`

### `spatial_validator.validate_scale(target_scale, node_name, scene_data)`

Pure function. Checks a target scale against sibling scales.

- `target_scale`: `list[float]` — 3-element [x, y, z]
- `node_name`: `str` — Name of the node being scaled
- `scene_data`: `dict` — Same format as above
- Returns: Same format as `validate_move`

### `node_ops(operations, scene_data=None)`

Internal bridge function. When `scene_data` is provided and any operation has `validate: true`, runs spatial validation before sending to the editor.

### `godotiq_node_ops(operations, scene=None, ctx=None)`

MCP tool wrapper. Resolves `scene` parameter to spatial data via session, then delegates to `node_ops`.

## Files

| File | Role |
|------|------|
| `src/godotiq/tools/bridge/spatial_validator.py` | Pure validation logic (stdlib only) |
| `src/godotiq/tools/bridge/node_ops.py` | Bridge integration (validation + bridge call) |
| `src/godotiq/tools/bridge/__init__.py` | MCP wrapper (session -> scene_data resolution) |
| `src/godotiq/prompts/godot_development.md` | AI agent documentation |
| `tests/test_bridge/test_spatial_validator.py` | 33 tests (unit + integration) |
