Skip to content
Symbiote Engine
Menu
Documentation Navigation
On this page

Guide

Dive deeper into the Symbiote Engine architecture: inputs & contracts, sequential execution pipelines, cache management modes, and graph history utilities.

Inputs & Contracts #

Symbiote Engine uses metadata contracts to represent data flowing between nodes. Sockets are declared using structured definitions:

  • Structured Definitions: Inputs and outputs are declared on the node's driver (using names and types).
  • Normalized Interfaces: Nodes read from a standardized input dictionary and output a plain JavaScript object.
  • Provider Contracts: Providers encapsulate execution capability. Host applications configure connection parameters, authentication, and execution environments. Executable providers require their own execute function. Generic input/parameter projection belongs only to node definitions without process or lifecycle.

Boundary & Validation Caveats

  • Registry state is module-global. Parameter constraints are partially checked, and the Executor does not invoke validation automatically.
  • Socket mismatch validation is only executed during connection, and only if both node types and both named sockets are registered.
  • Cycles are not detected during connection establishment.

Execution & Pipeline #

The Executor runs sequential traversal based on a Kahn topological sort of the graph's connections. Node execution routes through one of two execution paths based on registration:

1. Process-Only Execution #

If a node type is registered with a process function and no lifecycle, the executor runs the process function directly. If no process function is defined, it projects static parameters and current inputs directly to outputs.

2. Lifecycle Execution Path #

If a node type registers a lifecycle object, the executor bypasses the standard process function and executes this structured sequence:

  1. Validate: Runs optional node-level validation. If validation fails or throws an error, it returns a { _error: message } object, logs the error, and graph execution continues without halting.
  2. Cache Key: Generates a cache key for the lifecycle cache lookup.
  3. Lifecycle Cache Check: Inspects the executor-local cache using the node ID and computed key. If hit, cached outputs are reused.
  4. Execute: Runs the node execution logic.
  5. Post-Process: Runs optional synchronous post-processing.
  6. Store: Saves execution outputs to the local cache.

Cache Identity & Modes #

To prevent redundant computations, Symbiote Engine manages caching at two distinct layers:

Lifecycle Cache Modes #

The lifecycle cache is executor-local and keys items by node ID and a computed key. It supports three caching modes:

  • auto: Reuses outputs if inputs/parameters match the cached key.
  • freeze: Returns a lifecycle record only when one exists; otherwise execution proceeds.
  • force: Bypasses lifecycle lookup only and can still be pre-empted by incremental node-ID reuse.

Cache Key Default Behavior #

The default cache key is computed by stringifying the node's current inputs and parameters:

// Default cache key generation
JSON.stringify({ i: inputs, p: params })

Note that this default generation is a direct, un-hashed stringification. It is not canonicalized or sorted.

Incremental Executor Cache #

Separate from the lifecycle cache, the executor supports an incremental cache when cache: true is set.

  • If a node ID has prior cached outputs and is not explicitly marked dirty, the executor immediately reuses the outputs without comparing inputs.
  • This incremental check is performed before the lifecycle cache checks, meaning it takes priority over force mode. Setting cache: false disables incremental reuse but does not disable the lifecycle cache or make orphan/branch-skipped nodes execute.
  • Call clearCache() to clear both the incremental and lifecycle cache stores.

Storage & History #

Workflow state, caching, and editing history are transient and host-driven:

State Caching & Persistence #

There are three separate systems for state and persistence:

  • Serialization: serialize() and deserialize() operate on graph topologies. They exclude computed outputs (_output) by default and do not preserve execution cache, log, or dirtiness states.
  • Host Persistence Adapters: Host-supplied persistence adapters handle storage, including the transient memory adapter.
  • Node file helpers: Provided by Persistence.js, which is not the memory adapter. Its dormant dynamic Node imports remain in the browser module graph.

GraphHistory Utility #

The GraphHistory utility manages workflow revision history (up to 50 snapshots) for undo/redo actions:

import { Graph, GraphHistory } from 'symbiote-engine/browser';

const graph = new Graph();
const history = new GraphHistory();

// Push an initial snapshot of the graph to history
history.push([...graph.nodes.values()], graph.connections);

// Make a graph change
graph.addNode('docs/source', { value: 1 });

// Push a second snapshot
history.push([...graph.nodes.values()], graph.connections);

// Undo the change
const previousSnapshot = history.undo();

Crucial note: One snapshot alone makes undo() return null. The undo() and redo() methods return state snapshots containing only nodes and connections. Snapshots do not mutate the active Graph instance in-place. The caller must manually apply these snapshots back to their graph representation.