# Architecture Documentation

## Aperion Flow - The Conductor

> Pipeline & Workflow Engine for the Aperion Ecosystem

### Mission

Transform multi-step workflows into resilient, observable, crash-recoverable pipelines with LLM assistance integration.

---

## Core Architecture

```
┌─────────────────────────────────────────────────────────────────┐
│                        API Layer                                 │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────────┐   │
│  │ POST /start  │  │ GET /{id}    │  │ POST /resume         │   │
│  └──────────────┘  └──────────────┘  └──────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                     PipelineExecutor                             │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  DAG Traversal Engine                                   │    │
│  │  • Topological ordering                                 │    │
│  │  • Parallel execution (max_parallel)                    │    │
│  │  • Dependency resolution                                │    │
│  └─────────────────────────────────────────────────────────┘    │
│                              │                                   │
│         ┌────────────────────┼────────────────────┐             │
│         ▼                    ▼                    ▼             │
│  ┌────────────┐      ┌────────────┐      ┌────────────┐         │
│  │   Stage    │      │   Stage    │      │   Stage    │         │
│  │ Execution  │      │ Execution  │      │ Execution  │         │
│  └────────────┘      └────────────┘      └────────────┘         │
└─────────────────────────────────────────────────────────────────┘
                              │
        ┌─────────────────────┼─────────────────────┐
        ▼                     ▼                     ▼
┌──────────────┐      ┌──────────────┐      ┌──────────────┐
│  Recovery    │      │  Checkpoint  │      │    Event     │
│   Engine     │      │    Store     │      │   Emitter    │
│              │      │              │      │              │
│ • Retry      │      │ • Save       │      │ • flow.*     │
│ • Backoff    │      │ • Load       │      │ • stage.*    │
│ • Circuit    │      │ • Resume     │      │              │
│   Breaker    │      │              │      │              │
└──────────────┘      └──────────────┘      └──────────────┘
```

---

## Component Details

### 1. Pipeline Definitions (`definitions/`)

**Pipeline Model:**
- Immutable blueprint defining WHAT to execute
- DAG validation (cycle detection, dependency resolution)
- LLM assistance configuration per stage

```python
Pipeline(
    name="data_etl",
    stages=[...],
    max_parallel=4,      # Concurrent stage limit
    timeout=300.0,       # Total execution timeout
    fail_fast=True,      # Stop on first failure
)
```

**Stage Model:**
- Single unit of work with handler reference
- Dependencies define execution order
- Retry and timeout configuration

```python
PipelineStage(
    name="fetch_data",
    handler="app.handlers.fetch",
    depends_on=["init"],
    timeout=30.0,
    retry_count=3,
    llm_assistance=[
        LLMAssistanceConfig(mode=LLMAssistanceMode.ERROR_DIAGNOSIS)
    ],
)
```

### 2. Execution Engine (`engine/`)

**PipelineExecutor:**
- Async DAG traversal with `asyncio`
- Parallel execution respecting dependencies
- Handler loading (registry or dynamic import)
- Integration points for events, LLM, checkpoints

**PipelineContext:**
- Mutable state bag during execution
- Data flows between stages
- Stage result tracking
- Checkpoint creation/restoration

**RecoveryEngine:**
- Retry strategies (immediate, exponential, fixed)
- Circuit breaker per external dependency
- Failure classification (transient vs permanent)
- Fallback handler support

### 3. Strategic Registry (`definitions/registry.py`)

Pre-configured pipeline templates with intelligent LLM defaults:

| Category | Pipelines | Recommended LLM Modes |
|----------|-----------|----------------------|
| Testing | pytest_execution, coverage_analysis | PRE_VALIDATE, ERROR_DIAGNOSIS |
| Deployment | pre_deployment_validation, post_deployment_health | PRE_VALIDATE, ANALYZE_OUTPUT |
| Monitoring | metrics_analysis, alert_enrichment | ANALYZE_OUTPUT, ENRICH_CONTEXT |
| Code Analysis | lint_diagnosis, security_scan | ERROR_DIAGNOSIS, PRE_VALIDATE |

### 4. API Layer (`api/routes.py`)

FastAPI endpoints for pipeline management:

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/flow/start` | POST | Start new execution |
| `/flow/resume` | POST | Resume from checkpoint |
| `/flow/{id}` | GET | Get execution status |
| `/flow/{id}/data` | GET | Get output data |
| `/flow/` | GET | List executions |
| `/healthz` | GET | Health check |

---

## Integration Protocols

### EventEmitter Protocol

For integration with The Nervous System:

```python
class EventEmitter(Protocol):
    async def emit(
        self,
        event_type: str,       # e.g., "flow.stage.completed"
        payload: dict,         # Event data
        *,
        source: str | None,    # "aperion-flow"
        correlation_id: str | None,
    ) -> str:
        ...
```

**Emitted Events:**
- `flow.started` - Pipeline execution began
- `flow.stage.started` - Stage began
- `flow.stage.completed` - Stage succeeded
- `flow.stage.failed` - Stage failed
- `flow.completed` - Pipeline succeeded
- `flow.failed` - Pipeline failed
- `flow.cancelled` - Pipeline was cancelled

### LLMAssistant Protocol

For integration with The Switchboard:

```python
class LLMAssistant(Protocol):
    async def pre_validate(
        self,
        stage: PipelineStage,
        context: PipelineContext,
        timeout: float,
    ) -> dict:
        """Validate inputs before stage execution."""
        ...

    async def analyze_output(
        self,
        stage: PipelineStage,
        output: dict,
        context: PipelineContext,
        timeout: float,
    ) -> dict:
        """Interpret stage output."""
        ...

    async def diagnose_error(
        self,
        stage: PipelineStage,
        error: str,
        context: PipelineContext,
        timeout: float,
    ) -> dict:
        """Diagnose failure and suggest fixes."""
        ...
```

### CheckpointStore Protocol

For integration with The Cortex:

```python
class CheckpointStore(Protocol):
    async def save(self, checkpoint: Checkpoint) -> None: ...
    async def load(self, execution_id: str) -> Checkpoint | None: ...
    async def list_checkpoints(self, execution_id: str) -> list[Checkpoint]: ...
    async def delete(self, execution_id: str) -> None: ...
```

---

## Execution Flow

```
1. API receives StartPipelineRequest
   │
2. PipelineExecutor.execute(pipeline, initial_data)
   │
3. Create PipelineContext
   │
4. Emit "flow.started" event
   │
5. Save PIPELINE_START checkpoint
   │
6. Get topological order of stages
   │
7. For each ready stage (dependencies complete):
   │
   ├─► Run LLM pre_validate if configured
   │   │
   ├─► Execute handler with timeout
   │   │
   ├─► Run LLM analyze_output if configured
   │   │
   ├─► Update context with output
   │   │
   ├─► Emit "flow.stage.completed"
   │   │
   └─► Save STAGE_COMPLETE checkpoint
   │
8. If all stages complete:
   │
   ├─► Mark context completed
   ├─► Emit "flow.completed"
   └─► Save PIPELINE_COMPLETE checkpoint
   │
9. Return ExecutionResult
```

---

## Recovery Flow

```
1. API receives ResumePipelineRequest with execution_id
   │
2. Load latest checkpoint from store
   │
3. Create context from checkpoint
   │
   ├─► Restore data_snapshot
   ├─► Mark completed stages
   └─► Mark failed stages
   │
4. Resume execution from next pending stage
   │
5. Continue normal execution flow
```

---

## Security Model

### Secret Handling

**Never store secrets in PipelineContext.**

Use SecretRef pattern:

```python
# In stage handler
async def my_handler(data: dict) -> dict:
    # Resolve secret at runtime
    api_key = os.environ.get(data["api_key_ref"])
    # Use api_key...
    return {"result": "..."}

# In pipeline definition
PipelineStage(
    name="api_call",
    handler="app.api_call",
    metadata={"api_key_ref": "API_KEY"}  # Reference, not value
)
```

### Authentication

Integrate with The Gatekeeper for API protection:

```python
from gatekeeper.middleware.fastapi import require_permission

@router.post("/start")
async def start_pipeline(
    request: StartPipelineRequest,
    subject: Subject = Depends(require_permission("flow:execute")),
):
    ...
```

---

## Configuration

### Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `FLOW_MAX_PARALLEL` | Default max concurrent stages | 4 |
| `FLOW_DEFAULT_TIMEOUT` | Default pipeline timeout (seconds) | 300 |
| `FLOW_CHECKPOINT_STORE` | Store type (memory, redis, postgres) | memory |
| `FLOW_REDIS_URL` | Redis connection URL | - |
| `FLOW_LOG_LEVEL` | Logging level | INFO |

### Checkpoint Store Configuration

```python
# Redis
store = RedisCheckpointStore(
    url="redis://localhost:6379",
    prefix="flow:checkpoint:",
    ttl=86400 * 7,  # 7 days
)

# PostgreSQL
store = PostgresCheckpointStore(
    dsn="postgresql://user:pass@localhost/flow",
    table="checkpoints",
)
```

---

## Monitoring

### Recommended Metrics

```python
# Prometheus metrics
PIPELINE_EXECUTIONS = Counter(
    'flow_pipeline_executions_total',
    'Total pipeline executions',
    ['pipeline', 'status']
)

PIPELINE_DURATION = Histogram(
    'flow_pipeline_duration_seconds',
    'Pipeline execution duration',
    ['pipeline']
)

STAGE_DURATION = Histogram(
    'flow_stage_duration_seconds',
    'Stage execution duration',
    ['pipeline', 'stage']
)

ACTIVE_EXECUTIONS = Gauge(
    'flow_active_executions',
    'Currently running executions'
)

CIRCUIT_BREAKER_STATE = Gauge(
    'flow_circuit_breaker_state',
    'Circuit breaker state (0=closed, 1=open)',
    ['circuit']
)
```

### Health Checks

```bash
# Liveness
curl http://localhost:8001/healthz

# Readiness
curl http://localhost:8001/readyz
```

---

## Migration from aperion-legendary-ai

| Original File | New Location | Changes |
|---------------|--------------|---------|
| `pipelines/executor.py` | `engine/executor.py` | Async-first, stateless |
| `pipelines/pipeline.py` | `definitions/pipeline.py` | Pydantic v2, frozen |
| `pipelines/recovery.py` | `engine/recovery.py` | Protocol-based |
| `pipelines/strategic_registry.py` | `definitions/registry.py` | Same structure |

### Breaking Changes

1. ThreadPoolExecutor → asyncio
2. @with_doc_context removed (external service)
3. Internal service imports removed
4. All handlers must be async
