Execution

execute() runs the graph and returns a list with one entry per output node, in registration order:

results = g.execute()        # [output_1, output_2, ...]

Run order

Each output node is evaluated by walking backward through its dependencies (the executor recurses over a node’s upstreams before running the node). Consequences:

  • Shared results are computed once. If a node feeds more than one downstream, its result is cached after the first evaluation and reused

  • Dead branches don’t run. A node with no path to an output is never evaluated.

For an ordinary tool node the executor, per incoming edge, runs the upstream, obtains the edge’s connector, maps the upstream output into this node’s inputs (dropping None values), then runs the tool once and records its inputs and outputs to the run’s state file.

Map and decision nodes follow the same backward walk but add their own loop, running the tool once per scattered item (Gather nodes) or repeatedly until a condition holds (Decision nodes).

Input merging and key clashes

A node’s input dict is assembled by applying each incoming edge’s mapped result with dict.update, so when two edges contribute the same key, the one applied later overwrites the earlier. Two rules follow from the order the executor uses:

1. Input nodes override tool outputs. The executor processes a node’s non-input (tool) upstreams first, then its input nodes. Because input nodes are applied last, a value you set on an input node overwrites any value an upstream tool produced under the same key. If you pass input_pdb=... on an input node into a step that also receives a generated pdb from upstream, your input-node value wins, so set a key only when you mean to override.

2. Among tool edges, the last edge added wins. Tool upstreams are applied in the order they were wired with add_edge() (an edge appends to the downstream node’s upstream list). If two upstream tools emit the same key, the edge added last overwrites the earlier, so when two producers collide on a key, order the edge you want to win last.

Results and state

Each tool run writes to results/<tool>_output_N (N auto-increments by scanning the filesystem, so repeated runs never collide). Every node’s inputs and outputs are appended to a JSON state file as it runs, so intermediate products are preserved and inspectable even when they aren’t the declared output.

Output typing matters downstream: an output declared type = "dir" is returned as the directory path (so a connector can look inside it), not flattened into the list of files within it.