# Graph-Based Pipeline DSL

This document describes the new graph-based DSL for defining pipelines in Mainline.

## Overview

The graph DSL represents pipelines as nodes and connections, replacing the verbose `XYZStage` naming convention with a more intuitive graph abstraction.

## TOML Syntax

### Basic Pipeline

```toml
[nodes.source]
type = "source"
source = "headlines"

[nodes.camera]
type = "camera"
mode = "scroll"

[nodes.display]
type = "display"
backend = "terminal"

[connections]
list = ["source -> camera -> display"]
```

### With Effects

```toml
[nodes.source]
type = "source"
source = "headlines"

[nodes.noise]
type = "effect"
effect = "noise"
intensity = 0.5

[nodes.fade]
type = "effect"
effect = "fade"
intensity = 0.8

[nodes.display]
type = "display"
backend = "terminal"

[connections]
list = ["source -> noise -> fade -> display"]
```

### With Positioning

```toml
[nodes.source]
type = "source"
source = "headlines"

[nodes.position]
type = "position"
mode = "mixed"

[nodes.display]
type = "display"
backend = "terminal"

[connections]
list = ["source -> position -> display"]
```

## Python API

### Basic Construction

```python
from engine.pipeline.graph import Graph, NodeType

graph = Graph()
graph.node("source", NodeType.SOURCE, source="headlines")
graph.node("camera", NodeType.CAMERA, mode="scroll")
graph.node("display", NodeType.DISPLAY, backend="terminal")
graph.chain("source", "camera", "display")

pipeline = graph_to_pipeline(graph)
```

### With Effects

```python
from engine.pipeline.graph import Graph, NodeType

graph = Graph()
graph.node("source", NodeType.SOURCE, source="headlines")
graph.node("noise", NodeType.EFFECT, effect="noise", intensity=0.5)
graph.node("fade", NodeType.EFFECT, effect="fade", intensity=0.8)
graph.node("display", NodeType.DISPLAY, backend="terminal")
graph.chain("source", "noise", "fade", "display")

pipeline = graph_to_pipeline(graph)
```

### Dictionary/JSON Input

```python
from engine.pipeline.graph_adapter import dict_to_pipeline

data = {
    "nodes": {
        "source": "headlines",
        "noise": {"type": "effect", "effect": "noise", "intensity": 0.5},
        "display": {"type": "display", "backend": "terminal"}
    },
    "connections": ["source -> noise -> display"]
}

pipeline = dict_to_pipeline(data)
```

## CLI Usage

### Using Graph Config File

```bash
mainline --graph-config pipeline.toml
```

### Inline Graph Definition

```bash
mainline --graph 'source:headlines -> noise:noise:0.5 -> display:terminal'
```

### With Preset Override

```bash
mainline --preset demo --graph-modify 'add:noise:0.5 after:source'
```

## Node Types

| Type | Description | Config Options |
|------|-------------|----------------|
| `source` | Data source | `source`: "headlines", "poetry", "empty", etc. |
| `camera` | Viewport camera | `mode`: "scroll", "feed", "horizontal", etc. `speed`: float |
| `effect` | Visual effect | `effect`: effect name, `intensity`: 0.0-1.0 |
| `position` | Positioning mode | `mode`: "absolute", "relative", "mixed" |
| `display` | Output backend | `backend`: "terminal", "null", "websocket", etc. |
| `render` | Text rendering | (auto-injected) |
| `overlay` | Message overlay | (auto-injected) |

## Advanced Features

### Conditional Connections

```toml
[connections]
list = ["source -> camera -> display"]
# Effects can be conditionally enabled/disabled
```

### Parameter Binding

```toml
[nodes.noise]
type = "effect"
effect = "noise"
intensity = 1.0
# intensity can be bound to sensor values at runtime
```

### Pipeline Inspection

```toml
[nodes.inspect]
type = "pipeline-inspect"
# Renders live pipeline visualization
```

## Comparison with Stage-Based Approach

### Old (Stage-Based)

```python
pipeline = Pipeline()
pipeline.add_stage("source", DataSourceStage(HeadlinesDataSource()))
pipeline.add_stage("camera", CameraStage(Camera.scroll()))
pipeline.add_stage("render", FontStage())
pipeline.add_stage("noise", EffectPluginStage(noise_effect))
pipeline.add_stage("display", DisplayStage(terminal_display))
```

### New (Graph-Based)

```python
graph = Graph()
graph.node("source", NodeType.SOURCE, source="headlines")
graph.node("camera", NodeType.CAMERA, mode="scroll")
graph.node("noise", NodeType.EFFECT, effect="noise")
graph.node("display", NodeType.DISPLAY, backend="terminal")
graph.chain("source", "camera", "noise", "display")
pipeline = graph_to_pipeline(graph)
```

The graph system automatically:
- Inserts the render stage between camera and effects
- Handles capability-based dependency resolution
- Auto-injects required stages (camera_update, render, etc.)
