# Execution Dependency Rules

This document describes how FlowDrop determines when a node is ready to execute based on its incoming edges (dependencies).

## Overview

FlowDrop workflows consist of nodes connected by edges. Edges can be of different types:

| Edge Type | Purpose | Controls |
|-----------|---------|----------|
| **Trigger** | Controls execution flow | Whether a node executes at all |
| **Data** | Passes data between nodes | What input data a node receives |

The dependency rules determine:
1. **When** a node can execute (dependency satisfaction)
2. **What data** the node receives (data resolution)

## Edge Types

### Trigger Edges

Trigger edges connect to a node's **trigger input port** (typically named "trigger"). They control whether a node executes.

**Handle format:** `{nodeId}-input-trigger`

**Examples:**
- Gateway branches (If/Else, A/B, Switch)
- Sequential flow control
- Event-driven execution

### Data Edges

Data edges connect to a node's **data input ports** (any non-trigger port). They pass data from one node's output to another node's input.

**Handle format:** `{nodeId}-input-{portName}` (e.g., `logger.1-input-message`)

### Loopback Edges

Loopback edges connect to a node's **loopback input port** (e.g., `loop_back` on ForEach nodes). They represent loop-back connections for iterative processing.

**Handle format:** `{nodeId}-input-loop_back`

**Special behavior:**
- **Excluded from dependency checking** - they don't block node execution
- **Excluded from cycle detection** - allows visual loops without breaking topological sort
- **Rendered with dotted lines** in the workflow UI
- Used primarily by iterator nodes (ForEach) to receive results from loop body

## Dependency Rules

### Rule 1: Trigger Edges Use OR Logic

When a node has **trigger edges**, it executes when **at least ONE** trigger source is satisfied.

```
┌─────────────┐
│  Gateway    │
├─────────────┤
│ Branch A ───┼──trigger──┐
│ Branch B ───┼──trigger──┼──► [Node X]
└─────────────┘           │
                          │
[Other Node] ──trigger────┘

Node X executes when:
- Branch A is active, OR
- Branch B is active, OR  
- Other Node completes
```

**Rationale:** Trigger edges typically come from branching logic where only one path is active at a time.

### Rule 2: Data Edges to Different Ports Use AND Logic

When a node has data edges targeting **different input ports**, ALL ports must have at least one completed source.

```
[Source A] ──data──► port_x ─┐
                             ├──► [Compare Node]
[Source B] ──data──► port_y ─┘

Compare Node executes when:
- Source A completes (provides port_x), AND
- Source B completes (provides port_y)
```

**Rationale:** If a node declares multiple distinct inputs, it likely needs all of them to function correctly.

### Rule 3: Data Edges to Same Port Use OR Logic

When **multiple** data edges target the **same input port**, at least ONE source must complete.

```
[Template A] ──data──► message ─┐
                                ├──► [Logger]
[Template B] ──data──► message ─┘

Logger executes when:
- Template A completes, OR
- Template B completes
```

**Rationale:** Multiple sources providing the same type of data (same port) typically represent alternative paths—only one value is needed.

### Rule 4: Trigger Edges Take Priority

When a node has **both trigger and data edges**, only the trigger edges control execution timing. Data edges are used solely for data resolution.

```
[Gateway] ──trigger──────────────────► [Process Node]
                                            ↑
[Data Source] ──data──► input_data ─────────┘

Process Node executes when:
- Gateway trigger is satisfied

Data from Data Source is gathered IF it's available, but doesn't block execution.
```

## Combined Rules Summary

| Scenario | Logic | Description |
|----------|-------|-------------|
| Multiple triggers | **OR** | Execute when ANY trigger is satisfied |
| Multiple data → **same port** | **OR** | Execute when ANY source for that port completes |
| Multiple data → **different ports** | **AND** | Execute when ALL ports have at least one source |
| Trigger + Data edges | **Trigger controls** | Data edges don't block; just gather available data |
| Loopback edges | **Excluded** | Never block execution; used for loop iteration |

## Visual Examples

### Example 1: Branching Workflow (Works Correctly)

```
                    ┌──► [Template A] ──┐
[A/B Gateway] ──────┤                   ├──► [Logger] (message port)
                    └──► [Template B] ──┘
```

- A/B Gateway activates either branch A or B
- Template A or Template B executes (not both)
- Logger has two data edges to the same `message` port
- **Logger executes** when either Template completes (OR logic for same port)

### Example 2: Parallel Inputs (Waits for All)

```
[User Input] ──► name ───┐
                         ├──► [Greeting Generator]
[Time Service] ──► time ─┘
```

- Greeting Generator has two different ports: `name` and `time`
- **Generator waits** for BOTH sources (AND logic for different ports)

### Example 3: Mixed Trigger and Data

```
[Button Click] ──trigger──────────────────► [Send Email]
                                                 ↑
[Draft Email] ──data──► email_content ───────────┘
```

- Send Email has a trigger input and a data input
- **Executes when:** Button Click happens (trigger satisfied)
- **Data:** Gathers from Draft Email if available, but doesn't wait for it

## Data Resolution

When a node executes, it receives data from completed upstream nodes:

### Same Port Resolution (Multiple Sources)

When multiple sources target the same port and more than one has completed, the system uses the **most recently completed** source's value:

1. **Primary:** Latest execution order (higher counter = more recent)
2. **Fallback:** Alphabetical by node ID

A warning is logged when this occurs:
```
Multiple sources target the same input port 'message' on node 'logger.1'.
Previous source: 'template_a.1', current source: 'template_b.1'.
Using value from 'template_b.1' (resolved by latest execution order).
```

### Missing Data

If a source node hasn't completed (e.g., branch not taken), its data is simply not included. The node executes with whatever data IS available.

## Orchestrator-Specific Behavior

### Pipeline-Based Orchestrators

`SynchronousPipelineOrchestrator` and queue-based execution use `JobGenerationService::areJobDependenciesMet()` to determine job readiness:

- Checks trigger satisfaction (OR logic)
- Checks data dependencies per-port (OR within port, AND across ports)
- Jobs are queued when ready

### Direct Orchestrator

`SynchronousOrchestrator` uses pre-computed topological order:

- Nodes execute in dependency order
- `shouldExecuteNode()` checks trigger conditions
- Data is gathered from whatever executed parents are available
- Skipped nodes don't block downstream execution

## Best Practices

### DO: Use Same Port for Alternative Sources

```
[Option A] ──► input ─┐
                      ├──► [Processor]  ✅ Works with branching
[Option B] ──► input ─┘
```

### DO: Use Different Ports for Required Parallel Data

```
[Source X] ──► port_a ─┐
                       ├──► [Combiner]  ✅ Waits for both
[Source Y] ──► port_b ─┘
```

### DON'T: Connect Mutually Exclusive Sources to Different Ports

```
[Branch A] ──► port_a ─┐
                       ├──► [Node]  ❌ Will wait forever if Branch B taken
[Branch B] ──► port_b ─┘
```

**Solution:** Use same port or add trigger edges.

## Related Documentation

- [Internal Parameters](./internal-parameters.md) - Special parameter handling
- [Workflow Snapshots and Orchestrators](./workflow-snapshots-and-orchestrators.md) - Orchestrator details
- [Node Processor Development](./flowdrop-node-processor.md) - Creating custom nodes
