# Gateway Nodes and Branching

This document describes the gateway node system in FlowDrop, including the reserved `branches` input parameter, the `active_branches` output, and how branch port names are dynamically generated.

## Overview

Gateway nodes are special workflow nodes that control execution flow by routing execution to one or more conditional branches. They are analogous to BPMN gateways (exclusive, inclusive, or parallel).

### Key Characteristics

| Aspect | Description |
|--------|-------------|
| **Visual Type** | `gateway` - renders as a diamond shape |
| **Input Parameter** | `branches` - defines available output branches |
| **Output** | `active_branches` - specifies which branch(es) to execute |
| **Port Generation** | Branch names become dynamic output ports |

## Reserved Names

### `branches` (Input Parameter)

The `branches` parameter is a **reserved configuration key** that defines the available conditional output paths for a gateway node.

**Type:** `array` of branch objects

**Structure:**
```json
{
  "branches": [
    {
      "name": "True",
      "value": true
    },
    {
      "name": "False", 
      "value": false
    }
  ]
}
```

**Fields:**

| Field | Type | Description |
|-------|------|-------------|
| `name` | `string` | Display name for the branch (becomes the output port name) |
| `value` | `mixed` | The value to match against when determining which branch is active |

**Example configurations:**

```yaml
# Boolean Gateway (True/False)
branches:
  - name: 'True'
    value: true
  - name: 'False'
    value: false

# A/B Testing
branches:
  - name: 'A'
    value: 'a'
  - name: 'B'
    value: 'b'

# Switch Gateway (multiple options)
branches:
  - name: 'High'
    value: 'high'
  - name: 'Medium'
    value: 'medium'
  - name: 'Low'
    value: 'low'
  - name: 'Default'
    value: 'default'
```

### `active_branches` (Output)

The `active_branches` output is a **magic output name** that gateway node processors must return to indicate which branch(es) should be activated.

**Type:** `string` (single branch) or comma-separated string (multiple branches)

**Example return values:**

```php
// Single branch activation
return [
  "active_branches" => "true",   // Activates "True" branch
  // ... other outputs
];

// For A/B gateway
return [
  "active_branches" => "a",      // Activates "A" branch
  // ... other outputs
];

// Multiple branch activation (parallel gateway)
return [
  "active_branches" => "branch1,branch2",  // Activates both branches
  // ... other outputs
];
```

**Matching Logic:**

The orchestrator performs case-insensitive matching between `active_branches` and the branch `name` values:

```php
// From JobGenerationService.php
$active_branch_list = explode(',', strtolower($active_branches));
$active_branch_list = array_map('trim', $active_branch_list);

if (in_array(strtolower($branch_name), $active_branch_list, TRUE)) {
  // Branch is activated
}
```

## Dynamic Port Generation

When a node has `visual_type: gateway`, the frontend automatically generates output ports based on the `branches` configuration:

### Port Naming Convention

Branch names become output port names:

| Branch Name | Output Port Handle Format |
|-------------|--------------------------|
| `True` | `{nodeId}-output-True` |
| `False` | `{nodeId}-output-False` |
| `A` | `{nodeId}-output-A` |
| `Default` | `{nodeId}-output-Default` |

### Edge Resolution

When an edge connects from a gateway's branch output, the `branchName` is extracted from the source handle and stored in `WorkflowEdgeDTO`:

```php
// Edge: if_else.1-output-True → process_a.1-input-trigger
// branchName = "True"
```

This branch name is then used during execution to determine if the edge should be activated.

## Creating a Gateway Node Processor

### 1. Define the Parameter Schema

Include the `branches` parameter with appropriate defaults:

```php
public function getParameterSchema(): array {
  return [
    "type" => "object",
    "properties" => [
      // Your input parameters...
      "condition" => [
        "type" => "mixed",
        "title" => "Condition",
        "description" => "The condition to evaluate",
      ],
      // The branches parameter (reserved)
      "branches" => [
        "type" => "array",
        "description" => "The available branches",
        "readOnly" => TRUE,  // Value cannot be edited, but can be exposed
        "default" => [
          [
            "name" => "True",
            "value" => TRUE,
          ],
          [
            "name" => "False",
            "value" => FALSE,
          ],
        ],
        "items" => [
          "type" => "object",
          "properties" => [
            "name" => [
              "type" => "string",
              "description" => "The name of the branch",
            ],
            "value" => [
              "type" => "mixed",
              "description" => "The value of the branch",
            ],
          ],
        ],
      ],
    ],
  ];
}
```

### 2. Return `active_branches` in Process

```php
public function process(ParameterBagInterface $params): array {
  $condition = $params->get("condition");
  
  // Evaluate your condition
  $result = $this->evaluateCondition($condition);
  
  // Determine which branch to activate
  $activeBranch = $result ? "true" : "false";
  
  // MUST return active_branches
  return [
    "active_branches" => $activeBranch,
    "execution_metadata" => [
      "timestamp" => time(),
      "gateway_type" => "branch",
      "flow_control" => TRUE,
    ],
  ];
}
```

### 3. Configure Node Type

```yaml
id: my_gateway
label: 'My Gateway'
description: 'Custom gateway node'
category: logic
icon: 'mdi:source-branch'
plugin_version: 1.0.0
enabled: true
parameters:
  condition:
    configurable: false
    connectable: true
    required: true
  branches:
    configurable: true    # Allow users to modify branches
    connectable: false    # Branches are not runtime-connectable
    required: false
    default:
      - name: 'True'
        value: true
      - name: 'False'
        value: false
outputs: {}
tags: {}
executor_plugin: my_gateway
visual_type: gateway           # Renders as diamond with dynamic ports
supported_visual_types:
  - gateway
```

## Built-in Gateway Nodes

### Boolean Gateway

Simple true/false branching based on a boolean input.

| Parameter | Type | Description |
|-----------|------|-------------|
| `value` | `boolean` | The value to evaluate |
| `branches` | `array` | Default: True/False |

**Branches:** `True`, `False`

### If/Else

Conditional branching based on text comparison.

| Parameter | Type | Description |
|-----------|------|-------------|
| `data` | `mixed` | Input data to evaluate |
| `matchText` | `string` | Text to match against |
| `operator` | `string` | Comparison operator |
| `caseSensitive` | `boolean` | Case-sensitive comparison |
| `branches` | `array` | Default: True/False |

**Operators:** `equals`, `not_equals`, `contains`, `starts_with`, `ends_with`, `regex`

**Branches:** `True`, `False`

### A/B Gateway

Random A/B split for testing workflows.

| Parameter | Type | Description |
|-----------|------|-------------|
| `weight_a` | `number` | Weight for A (default: 50) |
| `weight_b` | `number` | Weight for B (default: 50) |
| `branches` | `array` | Default: A/B |

**Branches:** `A`, `B`

### Switch Gateway

Multi-way branching based on expression matching.

| Parameter | Type | Description |
|-----------|------|-------------|
| `expression` | `string` | The value to match |
| `default_branch` | `string` | Fallback branch name |
| `branches` | `array` | Configurable branch list |

**Branches:** User-configurable (e.g., `High`, `Medium`, `Low`, `Default`)

## Execution Flow

### How Branch Activation Works

1. **Gateway Executes:** The gateway processor evaluates its condition and returns `active_branches`
2. **Output Captured:** The orchestrator captures `active_branches` from the output
3. **Edge Evaluation:** For each downstream edge, the orchestrator checks if the edge's `branchName` matches `active_branches`
4. **Selective Execution:** Only nodes connected to active branches are executed

### Visual Representation

```
                    ┌──► [Process A] ── True branch
[Input] ──► [If/Else]
                    └──► [Process B] ── False branch

If active_branches = "true":
  ✓ Process A executes
  ✗ Process B is skipped

If active_branches = "false":
  ✗ Process A is skipped
  ✓ Process B executes
```

### Trigger Edge Behavior

Edges from gateway branch ports are treated as **trigger edges** when they connect to another node's trigger input:

```
[If/Else]-output-True ──► [Process A]-input-trigger
```

This follows the **OR logic** for triggers: a node executes when **any** of its trigger sources is satisfied.

## Frontend Integration

### FlowDrop UI Library

The `@flowdrop/flowdrop` library automatically:

1. Reads the `branches` configuration from `node.data.config.branches`
2. Generates output handles for each branch
3. Highlights active branches based on `node.data.executionInfo.output.active_branches`

### Handle Visibility

Branches can be conditionally hidden using the `isBranchVisible()` function in the frontend, which checks connected handles.

## Hybrid Nodes (Gateway + Data Outputs)

Some nodes benefit from acting as **both** a regular data node and a gateway. For example, a "Confirmation" (Human-in-Loop) node might want to:

1. Output `confirmed` as a boolean data value (for use in downstream data processing)
2. Also provide True/False branches (for flow control)

### Creating a Hybrid Node

A hybrid node simply outputs **both** regular data outputs **and** `active_branches`:

```php
public function process(ParameterBagInterface $params): array {
  // Evaluate the condition
  $confirmed = $this->evaluateConfirmation($params);
  
  // Return BOTH data outputs AND active_branches
  return [
    // Regular data output
    "confirmed" => $confirmed,
    "response_time" => time(),
    "user_id" => $this->getCurrentUserId(),
    
    // Gateway output for flow control
    "active_branches" => $confirmed ? "true" : "false",
  ];
}
```

### Node Type Configuration for Hybrid Nodes

Configure the node type to support **both** `default` and `gateway` visual types:

```yaml
id: confirmation
label: 'Confirmation'
description: 'Pauses workflow for user confirmation (Yes/No)'
category: human_in_loop
enabled: true
parameters:
  message:
    configurable: true
    connectable: true
    required: true
  # Include branches for gateway mode
  branches:
    configurable: true
    connectable: false
    required: false
    default:
      - name: 'True'
        value: true
      - name: 'False'
        value: false
outputs:
  # Regular data outputs
  confirmed:
    exposed: true
  response_time:
    exposed: true
  user_id:
    exposed: true
executor_plugin: confirmation
# Default to standard node appearance
visual_type: default
# Support BOTH regular and gateway modes
supported_visual_types:
  - default
  - simple
  - gateway    # Enable gateway mode
```

### Visual Mode Switching

When the user places a hybrid node in the workflow, they can switch between visual modes:

| Visual Mode | Behavior |
|-------------|----------|
| `default` / `simple` | Shows standard data outputs (`confirmed`, `response_time`, etc.) |
| `gateway` | Shows diamond shape with branch outputs (True, False) based on `branches` config |

**In both modes**, the processor outputs the same data - only the visual representation changes. This means:

- Downstream nodes connected to the `confirmed` data port receive the boolean value
- Downstream nodes connected to branch outputs (True/False) are triggered based on `active_branches`

### Example: Hybrid Confirmation Workflow

```
                                    ┌──► [Send Email] ── True branch
[Request] ──► [Confirmation (gateway)]
                                    └──► [Log Rejection] ── False branch

                                              │
                                              ▼ (confirmed output)
                                    [Analytics Logger]
```

In this example:
- When user confirms: `Send Email` executes (via True branch trigger)
- When user cancels: `Log Rejection` executes (via False branch trigger)
- `Analytics Logger` always receives the `confirmed` boolean value

### Benefits of Hybrid Nodes

1. **Flexibility**: Site builders can choose the visual representation that best fits their workflow
2. **Data + Flow Control**: The same node provides both data outputs and execution flow control
3. **Backward Compatibility**: Existing workflows using data outputs continue to work

## Best Practices

1. **Use Lowercase for Matching:** While branch names can use any case for display (e.g., "True"), the `active_branches` value should match case-insensitively.

2. **Single Active Branch:** For exclusive gateways, return only one branch name. For parallel gateways, return comma-separated names.

3. **Default Branch:** For switch gateways, always provide a default branch to handle unexpected values.

4. **ReadOnly Parameter:** Set `"readOnly": TRUE` on the branches parameter schema to prevent users from modifying the default value while still allowing the parameter to be exposed as a port.

5. **Consistent Naming:** Keep branch names simple and consistent (e.g., "True"/"False", "A"/"B", "Success"/"Failure").

6. **Consider Hybrid:** For nodes with boolean or enum outputs that affect flow, consider supporting both `default` and `gateway` visual types.

## Related Documentation

- [Reserved Names](./reserved-names.md) - List of all reserved names including `branches`
- [Execution Dependency Rules](./execution-dependency-rules.md) - How branching affects execution flow
- [FlowDrop Node Processor](./flowdrop-node-processor.md) - General node processor development
- [Internal Parameters](./internal-parameters.md) - System parameters like `__state__`

## See Also

- `modules/flowdrop_node_processor/src/Plugin/FlowDropNodeProcessor/BooleanGateway.php`
- `modules/flowdrop_node_processor/src/Plugin/FlowDropNodeProcessor/IfElse.php`
- `modules/flowdrop_node_processor/src/Plugin/FlowDropNodeProcessor/SwitchGateway.php`
- `modules/flowdrop_node_processor/src/Plugin/FlowDropNodeProcessor/AorB.php`
- `modules/flowdrop_pipeline/src/Service/JobGenerationService.php` (branch matching logic)
