Good -- no connections in main.tscn. Now I have comprehensive context. Let me generate the section content.

# Section 1: Value Parser Foundation

## Overview

This section creates the file `src/godotiq/parsers/value_parser.py`, a standalone module responsible for parsing the right-hand side of property assignments in Godot `.tscn` files. It has **no dependencies** on any other section and can be developed and tested in isolation. The state machine parser (Section 3) will import and call `parse_value()` for every property value it encounters.

The module must handle all Godot value types: scalars (bool, int, float, null, string), vector/math types (Vector2, Vector3, Color, AABB, Quaternion, etc.), reference types (ExtResource, SubResource, NodePath, StringName), packed arrays (stored as raw strings), arrays, dictionaries, and Transform3D decomposition into position/rotation/scale using YXZ Euler angles.

**Constraints from the project:**
- Python 3.10+, zero external dependencies (stdlib only -- no numpy, no scipy)
- All math (trig, sqrt) comes from the `math` module
- Malformed input is handled gracefully (return raw string rather than crashing)
- Performance-sensitive: `main.tscn` has megabyte-sized PackedByteArray lines that must not be fed through regex

## File to Create

**`/Users/salvatorefarruggio/Desktop/godotiq/src/godotiq/parsers/value_parser.py`**

## Tests (Write First)

Create the test file **`/Users/salvatorefarruggio/Desktop/godotiq/tests/test_parsers/test_value_parser.py`** with the following test stubs. Each test should call `parse_value()` imported from `godotiq.parsers.value_parser` and assert the expected return.

```python
# tests/test_parsers/test_value_parser.py
"""Unit tests for the TSCN value parser."""

import math
from godotiq.parsers.value_parser import parse_value, decompose_transform3d


# ──────────────────────────────────────────────
# Scalar parsing
# ──────────────────────────────────────────────

# Test: parse_value("true") returns True
# Test: parse_value("false") returns False
# Test: parse_value("null") returns None
# Test: parse_value("42") returns int 42
# Test: parse_value("3.14") returns float 3.14
# Test: parse_value("-3.059797e-08") returns float (scientific notation)
# Test: parse_value('"hello world"') returns "hello world" (string)

# ──────────────────────────────────────────────
# Vector / math types
# ──────────────────────────────────────────────

# Test: parse_value("Vector2(1.5, 2.0)") returns (1.5, 2.0)
# Test: parse_value("Vector3(1, 2, 3)") returns (1.0, 2.0, 3.0)
# Test: parse_value("Vector3i(1, 2, 3)") returns (1, 2, 3)
# Test: parse_value("Color(0.5, 0.3, 0.1, 1.0)") returns (0.5, 0.3, 0.1, 1.0)
# Test: parse_value("Rect2(0, 0, 100, 50)") returns tuple of 4 floats
# Test: parse_value("AABB(-1, -1, -1, 2, 2, 2)") returns tuple of 6 floats
# Test: parse_value("Quaternion(0, 0, 0, 1)") returns tuple of 4 floats

# ──────────────────────────────────────────────
# Reference types
# ──────────────────────────────────────────────

# Test: parse_value('ExtResource("1_abc")') returns dict {"type": "ext_resource", "id": "1_abc"}
# Test: parse_value('SubResource("BoxShape3D_1ufgv")') returns dict {"type": "sub_resource", "id": "BoxShape3D_1ufgv"}
# Test: parse_value('NodePath("Path/To/Node")') returns string "Path/To/Node"
# Test: parse_value('StringName(&"signal_name")') returns string "signal_name"

# ──────────────────────────────────────────────
# Packed arrays (stored as raw string, no decoding)
# ──────────────────────────────────────────────

# Test: parse_value('PackedByteArray("AAAB...")') stores as raw string (unchanged)
# Test: parse_value('PackedFloat32Array(1.0, 2.0)') stores as raw string (unchanged)

# ──────────────────────────────────────────────
# Arrays and dictionaries
# ──────────────────────────────────────────────

# Test: parse_value('["enemy", "damageable"]') returns ["enemy", "damageable"]
# Test: parse_value("[1, 2, 3]") returns [1, 2, 3]
# Test: parse_value with nested dict '{"key": "value"}' returns parsed dict

# ──────────────────────────────────────────────
# Transform3D decomposition
# ──────────────────────────────────────────────

# Test: identity → pos=(0,0,0), rot=(0,0,0), scale=(1,1,1)
# Test: translation only Transform3D(1,0,0, 0,1,0, 0,0,1, -36.117,0,-1.947)
#       → pos=(-36.117, 0, -1.947), rot=(0,0,0), scale=(1,1,1)
# Test: uniform scale 0.35 Transform3D(0.35,0,0, 0,0.35,0, 0,0,0.35, 0,0.326,0)
#       → scale=(0.35, 0.35, 0.35)
# Test: non-uniform scale Y=1.25 Transform3D(1,0,0, 0,1.25,0, 0,0,1, -37.617,0,-9.447)
#       → scale≈(1.0, 1.25, 1.0)
# Test: 90-degree Y rotation with non-uniform scale
#       Transform3D(-4.371139e-08,0,-1, 0,1.25,0, 1,0,-4.371139e-08, -37.617,0,-8.447)
#       → rot≈(0, -pi/2, 0), scale≈(1, 1.25, 1)
# Test: scientific notation noise cleaning
#       Transform3D(-3.059797e-08,0,0.7, 0,0.7,0, -0.7,0,-3.059797e-08, -36.7,0.687,1.507)
#       → rot≈(0, pi/2, 0), scale≈(0.7, 0.7, 0.7)
# Test: negative determinant (mirrored) → correct negative scale on one axis

# ──────────────────────────────────────────────
# Edge cases
# ──────────────────────────────────────────────

# Test: parse_value("truefalse") does NOT match bool — returns string or raw value
# Test: parse_value("") returns empty string
```

### Return format for reference types

ExtResource and SubResource references need to be distinguishable from plain strings so the state machine parser (Section 3) can extract IDs for node `script` and `instance` fields. Use a small dict representation:

- `ExtResource("1_abc")` returns `{"type": "ext_resource", "id": "1_abc"}`
- `SubResource("BoxShape3D_1ufgv")` returns `{"type": "sub_resource", "id": "BoxShape3D_1ufgv"}`

NodePath and StringName simply return the extracted string (downstream consumers only need the string value).

## Implementation Details

### Public API

The module exposes two public functions:

```python
def parse_value(raw: str) -> object:
    """Parse a TSCN property value string into a Python object.

    Handles all Godot value types: scalars, vectors, colors, references,
    arrays, dictionaries, packed arrays, and transforms.
    """
    ...

def decompose_transform3d(values: tuple[float, ...]) -> tuple[
    tuple[float, float, float],
    tuple[float, float, float],
    tuple[float, float, float],
]:
    """Decompose 12 Transform3D floats into (position, rotation, scale).

    Rotation is YXZ Euler angles in radians (matching Godot's convention).
    Scale accounts for determinant sign (negative scale / mirroring).
    """
    ...
```

### Type Detection Strategy (dispatch order)

The `parse_value` function should strip leading/trailing whitespace from `raw`, then dispatch using this priority order:

1. **Packed arrays** -- check first via `startswith` for any `Packed` prefix (e.g., `PackedByteArray`, `PackedFloat32Array`, `PackedVector3Array`, etc.). Return the raw string unchanged. This is a performance optimization: PackedByteArray values can be megabytes long and must never be fed through regex.

2. **Known type prefixes** -- check via `startswith` for: `Vector2(`, `Vector2i(`, `Vector3(`, `Vector3i(`, `Vector4(`, `Color(`, `Rect2(`, `AABB(`, `Transform3D(`, `Transform2D(`, `Basis(`, `Plane(`, `Quaternion(`, `ExtResource(`, `SubResource(`, `NodePath(`, `StringName(`. Extract the content inside parentheses and parse appropriately.

3. **Dictionary** -- starts with `{`. Parse key-value pairs recursively.

4. **Array** -- starts with `[`. Parse elements recursively.

5. **Quoted string** -- starts with `"`. Strip outer quotes, handle basic escape sequences (`\\`, `\"`, `\n`, `\t`).

6. **Scalar keywords** -- **exact full-string match only**: `"true"` returns `True`, `"false"` returns `False`, `"null"` returns `None`. The string `"truefalse"` must NOT match.

7. **Numeric** -- attempt `int()` first (no decimal point, no `e`/`E`), then `float()`. Handles scientific notation like `-3.059797e-08`.

8. **Fallback** -- return the raw string unchanged.

### Pre-compiled Regex Patterns

Use module-level `re.compile()` for patterns that are used repeatedly. Key patterns:

```python
# Extract content inside Type(...) for math types
_PARENS_RE = re.compile(r"^(\w+)\((.+)\)$", re.DOTALL)

# Extract quoted string inside reference types
_EXT_RESOURCE_RE = re.compile(r'^ExtResource\("([^"]+)"\)$')
_SUB_RESOURCE_RE = re.compile(r'^SubResource\("([^"]+)"\)$')
_NODE_PATH_RE = re.compile(r'^NodePath\("([^"]*)"\)$')
_STRING_NAME_RE = re.compile(r'^StringName\(&"([^"]*)"\)$')
```

### Parsing Math Types (Vector2, Vector3, Color, AABB, etc.)

For types like `Vector3(1, 2, 3)`:
1. Extract the content inside parentheses
2. Split by `,`
3. Convert each element to `float` (or `int` for `Vector3i`, `Vector2i`)
4. Return as a Python tuple

For `Vector2i` and `Vector3i`, parse as `int` instead of `float` to preserve the integer type.

For `Transform3D(...)`:
1. Extract 12 floats from inside the parentheses
2. Call `decompose_transform3d()` which returns `(position, rotation, scale)`
3. Return the result of decomposition as a dict: `{"type": "transform3d", "position": (...), "rotation": (...), "scale": (...)}`

For `Transform2D(...)`:
- Extract 6 floats and return as a plain tuple (decomposition is deferred to a future sprint)

### Transform3D Decomposition Algorithm

This is the most critical piece of math in the module. Given 12 floats from `Transform3D(b00, b10, b20, b01, b11, b21, b02, b12, b22, ox, oy, oz)`:

**Step 1 -- Extract position:**
```
position = (ox, oy, oz)  # the last 3 values
```

**Step 2 -- Reconstruct basis matrix (column-major to row-major):**
The TSCN format stores values column-major. The row-major basis matrix is:
```
row 0: [b00, b01, b02]
row 1: [b10, b11, b12]
row 2: [b20, b21, b22]
```
Concretely, given `values[0..11]`:
- Row 0 = `[values[0], values[3], values[6]]`
- Row 1 = `[values[1], values[4], values[7]]`
- Row 2 = `[values[2], values[5], values[8]]`

**Step 3 -- Clean floating-point noise:**
Any value with `abs(v) < 1e-7` snaps to `0.0`. This handles Godot's float32 precision artifacts like `-3.059797e-08` and `-4.371139e-08`.

**Step 4 -- Extract scale with determinant sign check:**
Column vector lengths give absolute scale:
```
sx = sqrt(row[0][0]^2 + row[1][0]^2 + row[2][0]^2)
sy = sqrt(row[0][1]^2 + row[1][1]^2 + row[2][1]^2)
sz = sqrt(row[0][2]^2 + row[1][2]^2 + row[2][2]^2)
```

Then compute the 3x3 determinant. If negative, negate `sx` (conventional choice for reflecting the sign on the X axis). This correctly handles mirrored/reflected objects.

**Step 5 -- Normalize basis:**
Divide each column by its (signed) scale to get a pure rotation matrix. Guard against zero scale: if a scale component is zero, skip normalization for that column and treat it as an identity column.

**Step 6 -- YXZ Euler decomposition (Godot's algorithm from `basis.cpp`):**
Given normalized matrix `m` (row-major):
```python
m12 = m[1][2]  # row 1, col 2

if abs(m12) < (1.0 - 0.00000025):  # not gimbal locked
    x = math.asin(-m12)
    y = math.atan2(m[0][2], m[2][2])
    z = math.atan2(m[1][0], m[1][1])
else:  # gimbal lock
    z = 0.0
    if m12 < 0:  # looking up
        x = math.pi / 2.0
        y = math.atan2(m[0][1], m[0][0])
    else:  # looking down
        x = -math.pi / 2.0
        y = math.atan2(-m[0][1], m[0][0])
```
The epsilon `0.00000025` matches Godot PR #102144.

**Step 7 -- Clean output angles:**
Snap any angle whose absolute value is less than `1e-10` to `0.0`.

Return `(position, rotation, scale)` as three tuples of three floats each.

### Real-world validation examples from fixture files

These examples from the actual fixture files validate the decomposition:

| Transform3D from fixture | Expected position | Expected rotation | Expected scale |
|---|---|---|---|
| `Transform3D(1,0,0, 0,1,0, 0,0,1, 0,0.5,0)` (worker.tscn CollisionShape3D) | (0, 0.5, 0) | (0, 0, 0) | (1, 1, 1) |
| `Transform3D(0.35,0,0, 0,0.35,0, 0,0,0.35, 0,0.326,0)` (fdm_printer.tscn PrinterModel) | (0, 0.326, 0) | (0, 0, 0) | (0.35, 0.35, 0.35) |
| `Transform3D(1,0,0, 0,1.25,0, 0,0,1, -37.617,0,-9.447)` (main.tscn SouthWall) | (-37.617, 0, -9.447) | (0, 0, 0) | (1, 1.25, 1) |
| `Transform3D(-4.371139e-08,0,-1, 0,1.25,0, 1,0,-4.371139e-08, -37.617,0,-8.447)` (main.tscn WestWall) | (-37.617, 0, -8.447) | (0, -pi/2, 0) | (1, 1.25, 1) |
| `Transform3D(-3.059797e-08,0,0.7, 0,0.7,0, -0.7,0,-3.059797e-08, -36.71,0.687,1.507)` (main.tscn computer_desk) | (-36.71, 0.687, 1.507) | (0, pi/2, 0) | (0.7, 0.7, 0.7) |

### Dictionary Value Parsing

Godot dictionaries appear in sub_resource properties (especially `_surfaces` blocks in ArrayMesh). Format:

```
{
"key": value,
"key2": value2
}
```

The parser should:
1. Strip outer `{` and `}`
2. Split on commas, respecting nesting depth (do not split inside nested `{}`, `[]`, or `()`)
3. For each key-value pair, split on the first `:` (with optional surrounding whitespace)
4. Parse the key (always a quoted string) and value (recursive call to `parse_value`)
5. Return a Python `dict`

A helper function for depth-aware comma splitting is needed: track depth across `()[]{}` and only split on commas at depth 0.

### Array Value Parsing

Arrays like `["enemy", "damageable"]` or `[{...}, {...}]`:
1. Strip outer `[` and `]`
2. Split on commas at depth 0 (same depth-aware splitting as dictionaries)
3. Parse each element via recursive `parse_value()`
4. Return a Python `list`

### Depth-Aware Comma Splitting

Both dictionary and array parsing need a shared helper that splits a string by commas while respecting nesting:

```python
def _split_at_depth_zero(s: str, delimiter: str = ",") -> list[str]:
    """Split string by delimiter only when bracket/brace/paren depth is zero.

    Tracks depth for: ( ) [ ] { }
    Also respects quoted strings (does not count brackets inside quotes).
    """
    ...
```

This function should:
- Track depth for `(`, `)`, `[`, `]`, `{`, `}`
- Track whether currently inside a quoted string (to avoid counting brackets in strings)
- Split on the delimiter only when depth is 0 and not inside quotes
- Return the list of parts, each stripped of leading/trailing whitespace

### Performance Optimization for PackedByteArray

Lines containing PackedByteArray values can be megabytes long. The `parse_value` function must check for `Packed` prefix via a simple `startswith` call **before** any regex matching. When detected, return the raw string immediately without further processing. This is critical for parsing `main.tscn` within the 200ms performance target.

### Error Handling

If a value cannot be parsed (e.g., malformed vector with wrong number of components), return the raw string unchanged rather than raising an exception. The parser should be robust against unexpected input. Use Python's `logging` module to emit a warning when falling back to raw string for a value that looked like it should have been a known type.

### Module Structure Summary

```python
"""Parser for Godot TSCN property values.

Handles all value types found on the right-hand side of property assignments
in .tscn files: scalars, vectors, colors, transforms, references, arrays,
dictionaries, and packed arrays.
"""

from __future__ import annotations

import math
import re
import logging

logger = logging.getLogger(__name__)

# Pre-compiled regex patterns
_EXT_RESOURCE_RE = ...
_SUB_RESOURCE_RE = ...
_NODE_PATH_RE = ...
_STRING_NAME_RE = ...
_PARENS_RE = ...

def parse_value(raw: str) -> object:
    """Parse a TSCN value string into a Python object."""
    ...

def decompose_transform3d(
    values: tuple[float, ...],
) -> tuple[tuple[float, float, float], tuple[float, float, float], tuple[float, float, float]]:
    """Decompose 12 Transform3D floats into (position, rotation, scale)."""
    ...

def _parse_array(inner: str) -> list[object]:
    """Parse the contents of a [...] array."""
    ...

def _parse_dict(inner: str) -> dict[str, object]:
    """Parse the contents of a {...} dictionary."""
    ...

def _split_at_depth_zero(s: str, delimiter: str = ",") -> list[str]:
    """Split string by delimiter respecting bracket/quote nesting."""
    ...

def _parse_float_tuple(inner: str, count: int) -> tuple[float, ...]:
    """Parse comma-separated floats from inside Type(...)."""
    ...

def _parse_int_tuple(inner: str, count: int) -> tuple[int, ...]:
    """Parse comma-separated ints from inside Type(...)."""
    ...
```

## Dependencies

This section has **no dependencies** on other sections. It can be implemented first, in parallel with Section 2 (Dataclass Definitions).

## What Depends on This Section

- **Section 3 (State Machine Parser)** imports `parse_value` to parse property values and header attribute values
- **Section 5 (Test Fixtures & Unit Tests)** validates value parsing indirectly through full parser tests