# Sprint 1 — TSCN Parser: Consolidated Specification

## Overview

Implement a complete parser for Godot 4.x `.tscn` (text scene) files. This is the most critical component of GodotIQ — 10+ MCP tools depend on it. The parser reads a `.tscn` file from disk, extracts all structural information, and returns a rich typed data structure with the full node hierarchy, resources, connections, and decomposed transforms.

## Context

- GodotIQ is a Python MCP server for Godot 4.x (Sprint 0 complete)
- Package layout: `src/godotiq/`, imports as `from godotiq.parsers.tscn_parser import ...`
- Python 3.10+, zero external dependencies (stdlib only)
- Existing placeholder: `parse_tscn(content: str) -> TscnScene` with `NotImplementedError`

## Design Decisions (from interview)

1. **API**: `parse_tscn(path: str) -> TscnScene` — takes file path, reads internally. Signature change from Sprint 0 placeholder (content → path).
2. **Dataclasses**: Extend `TscnNode` with new fields + add new types (`ExtResource`, `SubResource`, `Connection`, `SceneHeader`).
3. **Rotation units**: Radians (matches Godot internals).
4. **Large packed arrays**: Store as raw strings, don't decode.
5. **Query methods**: On `TscnScene` directly (`find_node()`, `get_nodes_by_group()`, etc.).
6. **Error handling**: Skip malformed lines with warning, continue parsing. Resilient over strict.

## Input/Output Contract

### Input
A file path (string) to a `.tscn` file on disk.

### Output: `TscnScene`

```
TscnScene
├── header: SceneHeader
│   ├── format: int (3 or 4)
│   ├── load_steps: int | None
│   └── uid: str | None
├── ext_resources: dict[str, ExtResource]
│   └── {id → ExtResource(type, path, uid)}
├── sub_resources: dict[str, SubResource]
│   └── {id → SubResource(type, properties)}
├── root: TscnNode (the tree root)
├── nodes: list[TscnNode] (flat list, all nodes)
├── connections: list[Connection]
└── query methods: find_node(), get_nodes_by_group(), get_nodes_by_type(), get_all_scripts(), get_all_instances()
```

### `TscnNode` (extended)

| Field | Type | Description |
|-------|------|-------------|
| `name` | `str` | Node name |
| `type` | `str` | Node type (empty if instanced) |
| `parent` | `str` | Parent path ("." = child of root, "" = is root) |
| `instance` | `str \| None` | ExtResource ID if instanced scene |
| `unique_id` | `int \| None` | Optional unique integer ID |
| `groups` | `list[str]` | Group memberships |
| `script` | `str \| None` | ExtResource ID if has script |
| `position` | `tuple[float,float,float] \| None` | Decomposed from Transform3D |
| `rotation` | `tuple[float,float,float] \| None` | Euler YXZ radians |
| `scale` | `tuple[float,float,float] \| None` | Scale factors |
| `properties` | `dict[str, object]` | All other properties |
| `metadata` | `dict[str, object]` | Keys with `metadata/` prefix |
| `children` | `list[TscnNode]` | Reconstructed hierarchy |

## .tscn Format Details (from research)

### Section Structure
Five section types, identified by `[type key=value ...]` headers:
1. `[gd_scene format=N uid="..." load_steps=N]` — file header
2. `[ext_resource type="T" path="res://..." id="ID" uid="..."]` — external refs
3. `[sub_resource type="T" id="ID"]` + property lines — internal resources
4. `[node name="N" type="T" parent="P" ...]` + property lines — scene nodes
5. `[connection signal="S" from="F" to="T" method="M" ...]` — signal connections

### Transform3D Layout
```
Transform3D(b00, b10, b20, b01, b11, b21, b02, b12, b22, ox, oy, oz)
```
- 12 floats: 3x3 basis matrix (column-major) + 3D origin
- Position: last 3 values (ox, oy, oz)
- Scale: column vector lengths of basis
- Rotation: normalize basis by scale, then YXZ Euler decomposition (Godot's default)

### Value Types to Parse
- Atomic: `null`, `true`/`false`, `int`, `float`, `"string"`
- Math: `Vector2(x,y)`, `Vector3(x,y,z)`, `Color(r,g,b,a)`, `Transform3D(...)`, `Rect2(...)`, `AABB(...)`, `Quaternion(...)`, `Basis(...)`, `Plane(...)`
- References: `ExtResource("id")`, `SubResource("id")`
- String-like: `NodePath("path")`, `StringName(&"name")`
- Collections: `PackedByteArray(...)`, `PackedFloat32Array(...)`, etc. → store as raw string
- Arrays: `[1, 2, 3]` — simple bracket arrays for groups etc.

## Test Fixtures

| File | Lines | Nodes | Key patterns |
|------|-------|-------|-------------|
| `worker.tscn` | 19 | 4 | Simple hierarchy, script, SubResource ref, transform with translation |
| `fdm_printer.tscn` | 29 | 6 | Multiple ext_resources, instances, groups, uniform scale transform |
| `furniture_slot_placeholder.tscn` | 37 | 6 | 3 sub_resources, Color, material refs, various property types |
| `main.tscn` | 4711 | 876 | Stress test: 68 ext, 142 sub, deep nesting, metadata, scientific notation, special names |

## Test Plan

### Unit Tests (23 tests in `test_tscn_parser.py`)
- Header parsing: format=3, format=4
- Ext/sub resource parsing with correct counts and properties
- Node basics: count, names, types
- Hierarchy: parent paths, children, deep nesting (6+ levels)
- Groups: single and multiple
- Transforms: identity, uniform scale, non-uniform, rotation, scientific notation
- Instance/script nodes
- Custom properties and metadata
- Value type parsing (Color, Vector3, int, float)
- Special node names (`@CSGBox3D@106885`)
- Stress tests: 876 nodes, 68 ext_resources

### Integration Tests (6 tests in `test_tscn_integration.py`)
- Full parse roundtrip (all fixtures, no exceptions)
- find_node_by_path query
- get_all_scripts extraction
- get_all_instances extraction
- get_nodes_by_group filtering
- get_nodes_by_type filtering

## Parser Architecture

Line-by-line state machine:
1. Read file, iterate lines
2. Detect section headers with `line.startswith('[')`
3. Parse header attributes with regex
4. Accumulate property lines until next section
5. Parse property values with type-specific regex
6. After flat parse: reconstruct parent-child tree in second pass

States: `INITIAL → FILE_HEADER → EXT_RESOURCE → SUB_RESOURCE → NODE → CONNECTION`

Pre-compiled regex patterns for performance. String methods for fast paths.

## File Structure

```
src/godotiq/parsers/
├── __init__.py
├── tscn_parser.py    # TscnScene, TscnNode, ExtResource, SubResource, Connection,
│                     # SceneHeader, parse_tscn(), query methods
└── value_parser.py   # parse_value(), Transform3D decomposition, type-specific parsers
```

## Constraints
- Zero external dependencies (stdlib only)
- Type hints everywhere, docstrings on all public API
- Performance: main.tscn (4711 lines) in < 200ms
- Sprint 0 tests must continue to pass (update test expectations for new signature)
- No business logic beyond parsing — no MCP tool implementation
