# How Workflow Edges Connect Nodes in the Modly Graph

> Discover how workflow edges connect nodes in the Modly graph using the WFEdge interface. Learn how Modly builds directed graphs for efficient execution and data flow.

- Repository: [lightningpixel/modly](https://github.com/lightningpixel/modly)
- Tags: internals
- Published: 2026-08-20

---

**In Modly, workflow edges connect nodes via the `WFEdge` interface which stores `source` and `target` node IDs, creating a directed graph that the execution engine traverses using helpers like `resolveDataSource` and `reachesSceneOutput`.**

The `lightningpixel/modly` repository implements a node-based workflow system where edges define the execution flow between operations. Understanding how these edges link nodes is essential for both authoring workflows programmatically and debugging execution paths.

## The WFEdge Interface Structure

The contract for workflow edges is defined in [`src/shared/types/electron.d.ts`](https://github.com/lightningpixel/modly/blob/main/src/shared/types/electron.d.ts). Each edge acts as a directed connection between two nodes, with optional port specifiers for multi-input or multi-output nodes.

```typescript
export interface WFEdge {
  id:            string            // unique edge identifier
  source:        string            // id of the node where the edge starts
  target:        string            // id of the node where the edge ends
  sourceHandle?: string | null    // optional port on the source node (e.g. "output")
  targetHandle?: string | null    // optional port on the target node (e.g. "input-0")
}

```

This interface appears at lines 23–29 of the type definitions file. The `source` and `target` fields reference the `id` properties of `WFNode` objects, while the optional handle fields enable precise port-to-port connections required by React Flow rendering.

## Creating and Storing Workflow Edges

When workflows are constructed or migrated from legacy formats, Modly generates the edge list in [`src/shared/stores/workflowsStore.ts`](https://github.com/lightningpixel/modly/blob/main/src/shared/stores/workflowsStore.ts). The store chains nodes sequentially by mapping each node to its successor:

```typescript
const edges: WFEdge[] = allNodes.slice(0, -1).map((n, i) => ({
  id:     `e-${n.id}-${allNodes[i + 1].id}`,
  source: n.id,
  target: allNodes[i + 1].id,
}))

```

This logic appears at lines 152–156. The resulting array populates the workflow's `edges` collection, which is persisted alongside the `nodes` array.

### Edge Sanitization and Validation

The `sanitizeEdges` function (lines 101–123 in the same file) enforces graph integrity through three specific safeguards:

- **Dangling endpoint removal** – Edges referencing non-existent source or target nodes are filtered out immediately
- **Handle patching** – For extension nodes, missing `sourceHandle` or `targetHandle` values are populated with defaults (`'output'` and `'input-0'` respectively) to ensure React Flow can resolve ports
- **Type-based constraints** – The system consults `NODE_TYPES_WITHOUT_TARGET` and `NODE_TYPES_WITHOUT_SOURCE` sets to prevent creating edges that would connect to nodes lacking the required handles (for example, preventing an `outputNode` from acting as a source)

## Traversing the Graph at Runtime

During execution, the engine interprets edges as directed pathways through the workflow. The helpers in [`src/areas/workflows/nodeBehaviors.ts`](https://github.com/lightningpixel/modly/blob/main/src/areas/workflows/nodeBehaviors.ts) navigate these connections without modifying the graph structure.

### Resolving Data Sources

The `resolveDataSource` function (lines 43–57) walks backward from a given node, following `source` references through any *passthrough* nodes until it locates the actual upstream data producer. This traversal relies solely on matching `source` IDs to node IDs in the workflow collection.

### Finding Upstream Dependencies

`nearestUpstreamWaits` (lines 65–80) identifies the closest `Wait` nodes that feed into the current node. It uses the edge list to trace incoming connections recursively, gathering all branch-starter nodes that must complete before execution can proceed.

### Checking Output Reachability

To determine if a node contributes to final scene output, `reachesSceneOutput` (lines 90–108) performs a forward traversal following `target` references. It respects edge direction and branch boundaries to verify whether any path from the starting node eventually terminates at a scene-output node.

## Summary

- **Modly stores workflow edges** as `WFEdge` objects containing `source` and `target` node IDs, with optional handle identifiers for precise port mapping
- **Edge creation** occurs in [`workflowsStore.ts`](https://github.com/lightningpixel/modly/blob/main/workflowsStore.ts), where nodes are chained sequentially or sanitized to remove dangling references and patch missing handles
- **Runtime traversal** uses directional edge walking via `resolveDataSource`, `nearestUpstreamWaits`, and `reachesSceneOutput` to determine execution order and data dependencies
- **Validation constants** like `NODE_TYPES_WITHOUT_TARGET` prevent invalid connections during graph construction

## Frequently Asked Questions

### What happens if an edge references a deleted node?

The `sanitizeEdges` function in [`src/shared/stores/workflowsStore.ts`](https://github.com/lightningpixel/modly/blob/main/src/shared/stores/workflowsStore.ts) automatically removes any edge whose `source` or `target` ID does not exist in the current `nodes` array. This prevents runtime errors during graph traversal by ensuring the edge list always references valid endpoints.

### Can a node have multiple input or output edges?

Yes. The `WFEdge` interface supports multiple connections through the `sourceHandle` and `targetHandle` fields. A single node can be the `target` of multiple edges (multiple inputs) or the `source` of multiple edges (branching outputs), provided the node type supports the corresponding handles and passes validation against `NODE_TYPES_WITHOUT_SOURCE` or `NODE_TYPES_WITHOUT_TARGET`.

### How does Modly handle edges for extension nodes that lack defined ports?

The `sanitizeEdges` function detects when `sourceHandle` or `targetHandle` are `null` on extension nodes and patches them with default values (`'output'` for sources, `'input-0'` for targets). This ensures compatibility with React Flow's rendering engine while maintaining the directed graph structure required for execution.

### What is the difference between sourceHandle and targetHandle?

`sourceHandle` identifies the specific output port on the source node (such as `"output"`), while `targetHandle` identifies the input port on the target node (such as `"input-0"`). These optional fields allow precise connections when nodes expose multiple ports, enabling complex data routing beyond simple one-to-one node linking.