# Sprint 6a — Complete Specification: godotiq_trace_flow

## Overview

Implement `godotiq_trace_flow` — a pure Python, filesystem-only MCP tool that traces how a game action flows through a Godot project's codebase. Given a trigger (function name or signal name), it follows the complete chain of function calls and signal emissions across all project files, returning the flow with steps and failure points.

This is the #1 unique differentiator for GodotIQ. No other Godot MCP tool can trace a game action through the codebase.

After this sprint: **31 MCP tools**.

---

## Functional Requirements

### Input
- **trigger** (str): Exact function name or signal name to trace from. Examples: "accept_order", "order_accepted", "printer_done"
- **project_path** (str): Path to Godot project root. Empty = use session default.
- **depth** (int): Maximum trace depth, default 10. Prevents infinite loops.
- **detail** (str): "brief" | "normal" | "full"

### Output
```python
{
    "flow": [
        {
            "file": "res://scripts/order_manager.gd",
            "function": "accept_order",
            "line": 184,
            "action": "Emits: order_accepted",
            "emits": ["order_accepted"],
            "calls": []
        },
        # ... more steps
    ],
    "failure_points": [
        {"step": 0, "if": "order_id not in _orders", "then": "returns false, flow stops"}
    ],
    "files_involved": 5,
    "total_steps": 12
}
```

### Detail Levels
- **brief**: `flow` list with file, function, action only. No failure_points.
- **normal**: Full flow + failure_points.
- **full**: Same as normal (metadata only per interview decision — no source snippets).

### Matching Rules
- **Exact match only** for trigger names (no substring/fuzzy matching)
- **Signal source stripping**: Parse `GdSignalConnection.signal_source` by splitting on `.` and taking the last part. "Events.order_accepted" → "order_accepted"
- **Multiple entry points**: Trace from ALL matches, return all flows combined

### Scope
- **Code connections only** (.connect() calls in .gd files). No .tscn editor connections for v1.
- **Failure detection**: Simple regex heuristics for early returns and null checks.

---

## Architecture

### Data Flow
```
trigger string
    ↓
_find_trigger() → list of (file, function) entry points
    ↓
For each entry point:
    _trace_from_function() → recursive DFS with:
        - signal emission following (via _build_signal_listeners map)
        - autoload call following (via ProjectIndex.autoloads)
        - cycle detection (visited set)
        - depth limiting
        - failure point detection (regex heuristics)
    ↓
Combine all flow steps + failure points
    ↓
Return dict with flow, failure_points, files_involved, total_steps
```

### Key Data Structures

**From gd_parser.py** (use as-is, do NOT modify):
- `GdScript.signal_emissions: list[GdSignalEmission]` — `.signal_name`, `.line`
- `GdScript.signal_connections: list[GdSignalConnection]` — `.signal_source`, `.target`, `.line`
- `GdScript.autoload_refs: list[GdAutoloadRef]` — `.autoload_name`, `.access`, `.line`
- `GdScript.functions: list[GdFunction]` — `.name`, `.line`, `.end_line`
- `GdScript.signals: list[GdSignal]` — `.name`, `.line`

**From session.py** (primary interface):
- `session.get_script(res_path) → GdScript | None`
- `session._gd_scripts: dict[str, GdScript]` — all parsed scripts
- `session._index.autoloads: dict[str, str]` — autoload_name → res://path

### Signal Listener Map
Build once per trace call:
```python
def _build_signal_listeners(scripts: dict[str, GdScript]) -> dict[str, list[tuple[str, str]]]:
    """signal_name → [(file_path, handler_function)]"""
    # For each script's signal_connections:
    #   Parse signal_source → extract signal_name (last part after '.')
    #   target = connection.target (handler function name)
    #   Add (file_path, target) to listeners[signal_name]
```

### Recursive Tracing
```python
def _trace_from_function(file, func_name, scripts, listeners, autoloads, visited, depth, max_depth):
    # Base cases: depth exceeded, already visited (cycle), script/func not found
    # Find function in script by exact name match
    # Collect emissions within function's line range (line..end_line)
    # Collect autoload calls within function's line range
    # Build FlowStep
    # Recursively follow: emitted signals → their listeners, autoload calls → their functions
    # Return list of FlowSteps
```

### Failure Point Detection
Simple regex on function source lines between `func.line` and `func.end_line`:
- Pattern: `if not X: return` → "if not X, flow stops"
- Pattern: `if X == null: return` → "if X is null, flow stops"
- Pattern: `if X.is_empty(): return` → "if X is empty, flow stops"
- Pattern: `if not is_instance_valid(X): return` → "if X is invalid, flow stops"

Requires access to raw source lines. Read the .gd file or check if GdScript stores raw lines.

---

## File Layout

### Files to CREATE
| File | Purpose |
|------|---------|
| `src/godotiq/tools/flow/__init__.py` | MODIFY (exists) — add trace_flow implementation |
| `tests/test_tools/test_trace_flow.py` | New test file |
| `tests/fixtures/trace_flow/project.godot` | Minimal project with autoloads |
| `tests/fixtures/trace_flow/events.gd` | Signal bus |
| `tests/fixtures/trace_flow/order_manager.gd` | Emits order_accepted |
| `tests/fixtures/trace_flow/pipeline_controller.gd` | Connects to order_accepted, calls PrinterManager |
| `tests/fixtures/trace_flow/printer_manager.gd` | Handles reserve_printer, emits printer_reserved |

### Files to MODIFY
| File | Change |
|------|--------|
| `src/godotiq/tools/flow/__init__.py` | Add trace_flow helper function |
| `src/godotiq/server.py` | Register godotiq_trace_flow tool |
| `src/godotiq/prompts/godot_development.md` | Document the tool |

### Files NOT to touch
- `godot-addon/` — no GDScript changes
- `src/godotiq/parsers/` — use, don't modify
- `src/godotiq/resolvers/` — use, don't modify
- `src/godotiq/tools/bridge/` — not a bridge tool
- Existing test files

---

## Test Plan

### Fixture Design
Create `tests/fixtures/trace_flow/` with minimal .gd files that form a signal chain:
- `events.gd`: Signal bus autoload with signal definitions
- `order_manager.gd`: `func accept_order()` emits `order_accepted`
- `pipeline_controller.gd`: Connects to `order_accepted`, calls `PrinterManager.reserve_printer()`
- `printer_manager.gd`: `func reserve_printer()` emits `printer_reserved`
- `project.godot`: Declares autoloads for Events, PrinterManager

### Test Cases
1. **test_find_trigger_by_function_name** — exact match finds the function
2. **test_find_trigger_by_signal_name** — finds functions that emit the signal
3. **test_trace_single_step** — function with no outgoing connections → 1 step
4. **test_trace_signal_chain** — A emits → B listens → B emits → C listens
5. **test_trace_autoload_call** — A calls PrinterManager.method → traces into printer_manager.gd
6. **test_depth_limit** — trace stops at configured depth
7. **test_cycle_detection** — A → B → A doesn't infinite loop
8. **test_trigger_not_found** — unknown trigger returns empty flow
9. **test_multiple_listeners** — signal with multiple listeners → multiple branches
10. **test_detail_brief** — brief detail returns fewer fields
11. **test_failure_point_detection** — detects early return patterns

### Verification
1. `pytest tests/ -v` → ALL 695+ existing tests pass
2. New trace_flow tests pass
3. Import check: `python -c "from godotiq.tools.flow import _build_trace_flow; print('OK')"`

---

## Decisions Summary

| Decision | Choice | Rationale |
|----------|--------|-----------|
| Signal source matching | Strip prefix, match name only | Catches connections regardless of source object |
| TSCN connections | Code-only for v1 | Simpler, can extend later |
| Failure detection | Simple regex heuristics | Good enough for v1, easy to extend |
| Trigger matching | Exact match only | Predictable, avoids false positives |
| File layout | flow/__init__.py + server.py | Follows existing code tool convention |
| Multiple entry points | Trace all matches | Shows complete picture |
| Output content | Metadata only | Compact, no source snippets |
