Forward vs Dependency Execution Paths in Ceres Flow: A Complete Guide

Forward execution follows explicit control flow wiring between nodes, while Dependency execution automatically resolves data dependencies using topological sorting before running any consuming node.

Ceres Flow, the visual scripting system in the akikurisu/ceres repository, provides two distinct execution models that determine how nodes in a graph are evaluated. Understanding the difference between Forward and Dependency execution paths is essential for building efficient visual scripts that balance imperative control flow with automatic data resolution.

What Are the Execution Paths in Ceres Flow?

Ceres Flow evaluates graphs using two complementary strategies. The Forward execution path processes nodes sequentially according to explicit exec port connections that you wire manually. The Dependency execution path analyzes input requirements at runtime and automatically executes provider nodes first, regardless of their position in the forward chain.

These paths are not mutually exclusive—a single graph can contain nodes that use both paradigms. The engine handles the coordination through ExecutionContext, which either moves to the next wired node (Forward) or resolves data dependencies via cached topological sorts (Dependency).

Forward Execution Path

How Forward Execution Works

The Forward path follows the explicit execution chain that you wire together using the exec output ports of FlowNode instances. When a node completes its work, it calls executionContext.Forward(nextNode) to advance to the next node in the sequence.

According to the source code in Runtime/Flow/Models/Nodes/Core/ExecutableNode.cs, nodes that use this path inherit from ForwardNode and implement a sequential control flow. The graph walks from node to node exactly as drawn on the canvas, performing no analysis of data dependencies during traversal.

Implementing Forward Nodes

Forward nodes typically handle imperative logic, sequenced actions, loops, and branching. They declare an exec output port and manually trigger the next node in the chain.

public class FlowNode_Sequence : ForwardNode
{
    [OutputPort(false), CeresLabel("")]
    public NodePort exec;          // explicit forward port

    protected override UniTask Execute(ExecutionContext ctx)
    {
        // Perform work here
        DoWork();
        
        // Move to the next node explicitly
        ctx.Forward(exec.GetT<ExecutableNode>());
        return UniTask.CompletedTask;
    }
}

In this pattern, ctx.Forward(...) drives the execution flow. The node determines when and where execution continues, giving you precise control over program order.

Dependency Execution Path

How Dependency Execution Works

The Dependency path runs all data-dependency nodes first before executing any node that consumes their results. When the engine encounters a node requiring specific inputs, it examines the node's input ports, builds a topologically sorted list of provider nodes, and executes that list automatically.

As implemented in Runtime/Core/Models/Graph/CeresGraph.cs, the system maintains a cached dependency path (_nodeDependencyPath) for each node. When execution requires a specific node's data, ExecutionContext.ExecuteDependencyPath(guid) retrieves this cached list and runs each dependency in order, guaranteeing that every required value is computed before the requesting node runs.

Implementing Dependency Functions

Nodes that provide data rather than control flow use the [ExecutableFunction(ExecuteInDependency = true)] attribute defined in Runtime/Flow/Annotations/ExecutableFunctionAttribute.cs. These nodes lack exec ports and rely entirely on the engine's data dependency resolution.

public class GetPlayerPosition : FlowNode
{
    // No exec port – it’s a data provider
    [OutputPort, CeresLabel("Position")]
    public NodePort output;

    [ExecutableFunction(ExecuteInDependency = true), CeresLabel("Get Player Position")]
    protected override void LocalExecute(ExecutionContext ctx)
    {
        output.SetValue(Player.instance.Position);
    }
}

Because ExecuteInDependency = true, the graph executes this node automatically before any node that consumes the "Position" value, regardless of forward wiring or canvas position.

Behind the Scenes: Topological Sorting

The actual dependency resolution occurs in Runtime/Flow/Models/ExecutionContext.cs. The ExecuteDependencyPath method retrieves the pre-calculated dependency chain and executes each node:

private async UniTask ExecuteDependencyPath(string guid, CancellationToken token)
{
    var path = Graph.GetNodeDependencyPath(guid);   // top-sorted list of dependent nodes
    foreach (var idx in path)
    {
        var node = Graph.nodes[idx];
        await node.ExecuteNode(this);               // runs each dependency node
    }
}

The CeresGraph builds these paths once via topological sort and reuses them for every execution, ensuring optimal performance for data-driven graphs.

Key Differences Between Forward and Dependency Paths

Understanding when to use each path requires recognizing their fundamental architectural differences:

  • Forward = You dictate the control flow through explicit exec port wiring. Nodes visit one-by-one exactly as arranged on the forward-execution line.
  • Dependency = The engine determines execution order needed to satisfy data dependencies, automatically resolving provider nodes through cached topological sorts.

Forward execution excels at imperative logic, UI sequences, and branching narratives where order matters more than data relationships. Dependency execution optimizes "getter" nodes, pure functions, and data transformations where inputs must be ready before computation begins.

When to Use Each Execution Path

Use Forward execution when building:

  • Sequential gameplay events that must happen in a specific order
  • Branching dialogue trees or quest logic
  • Loop constructs and iterative processes
  • Any logic requiring manual control over execution timing

Use Dependency execution when building:

  • Data provider nodes (e.g., Get Player Position, Read Variable)
  • Pure calculation functions with input dependencies
  • Reactive systems where outputs automatically update when inputs change
  • Complex mathematical graphs where manual wiring would be error-prone

Summary

  • Forward execution paths in Ceres Flow follow explicit exec port wiring that you control manually, using ExecutionContext.Forward() to advance between nodes.
  • Dependency execution paths automatically resolve data requirements using topological sorts cached in CeresGraph._nodeDependencyPath.
  • Nodes inherit from ForwardNode for manual control or use [ExecutableFunction(ExecuteInDependency = true)] for automatic data resolution.
  • The engine coordinates both paths through ExecutionContext.ExecuteDependencyPath(), which runs dependency chains before the requesting node executes.
  • Choose Forward for imperative control flow and Dependency for data-driven, reactive computation.

Frequently Asked Questions

What is the main difference between Forward and Dependency execution in Ceres Flow?

Forward execution requires manual wiring through exec ports and visits nodes in the exact order you specify on the canvas. Dependency execution automatically analyzes input requirements and executes provider nodes first via topological sorting, regardless of their canvas position or forward connections.

How does Ceres Flow determine which nodes to run first in Dependency mode?

The engine consults the dependency cache stored in CeresGraph at _nodeDependencyPath. When building the graph, Ceres performs a topological sort on data dependencies and stores the result. During execution, ExecutionContext.ExecuteDependencyPath() retrieves this cached list and runs each node in dependency order.

Can a single node use both Forward and Dependency execution paths?

Yes. A node can inherit from ForwardNode while also having input ports that trigger Dependency execution. In this case, the engine first resolves all data dependencies automatically, then executes the node's Forward logic, and finally continues along the exec output chain you have wired.

Where is the dependency execution logic implemented in the Ceres source code?

The core logic resides in Runtime/Flow/Models/ExecutionContext.cs, specifically within the ExecuteDependencyPath method. The topological sorting and caching mechanism is implemented in Runtime/Core/Models/Graph/CeresGraph.cs using the _nodeDependencyPath dictionary. The attribute that marks functions for Dependency execution is defined in Runtime/Flow/Annotations/ExecutableFunctionAttribute.cs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →