engine

package
v0.0.0-...-0d68cb6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

README

Go Temporal Workflow Graph Interpreter Engine

A powerful, JSON-DSL-driven graph interpreter engine built on top of the Go Temporal SDK. This engine dynamically executes complex directed-acyclic-graph (DAG) workflows defined in a JSON specification without requiring code redeployment.

Key Features

  • DSL-Driven DAG Execution: Runs workflows represented by structured nodes and conditional edges.
  • Multiple Node Types:
    • START / END: Standard execution entry and exit points.
    • TASK: Executes application activities. Supports synchronous/asynchronous work execution.
    • GATEWAY: Controls logical branching and joining:
      • Control flow: EXCLUSIVE_SPLIT, PARALLEL_SPLIT, EXCLUSIVE_JOIN, PARALLEL_JOIN
      • Batch partitioning: BATCH_SPLIT (partitions item slices by evaluating edge conditions per-item and spawns composable child workflows), BATCH_JOIN (merges child results by item ID)
    • SPLIT_TASK: Spawns multiple parallel child workflows dynamically (dynamic fan-out). Supports:
      • SAME_TEMPLATE (Deprecated: Use BATCH_SPLIT instead for inline DAG sub-graphs, aggregated tasks, and item-ID merging)
      • DIFFERENT_TEMPLATES: Poly-workflow / heterogeneous splits running different external templates dynamically.
      • Failure handling configurations (FAIL_FAST or COLLECT_ALL).
    • SIGNALING: Coordinates sibling branches spawned by the same SPLIT_TASK node without invoking any external activity. Two sub-types:
      • EMIT: Fires a signal asynchronously to all sibling branches (one hop up to the parent, one hop back down).
      • WAIT: Blocks the branch until a named signal is received from a sibling.
  • Flexible Data Mapping: Automatically maps keys between the global WorkflowVariables dictionary and task input/output scopes using InputMapping and OutputMapping.
  • Query & Signal Support: Provides real-time queries for workflow execution state snapshots (WorkflowInstance) and processes external update events asynchronously.

Architecture Overview

graph TD
    Start([Start Node]) --> Task1[Task Node]
    Task1 --> Gate1{Gateway: Split}
    Gate1 -->|Condition A| SigWait[SIGNALING: WAIT]
    Gate1 -->|Condition B| SigEmit[SIGNALING: EMIT]
    SigWait --> Gate2{Gateway: Join}
    SigEmit --> Gate2
    Gate2 --> Split1[Split Task Node]
    Split1 -->|Child Workflow 1| Child1[Child Instance]
    Split1 -->|Child Workflow 2| Child2[Child Instance]
    Child1 --> End([End Node])
    Child2 --> End

DSL Specification (dsl.go)

Workflows are defined through the WorkflowDefinition structure.

Node Config Definition
type Node struct {
	ID             string            `json:"id"`
	Type           NodeType          `json:"type"`                       // START, END, TASK, GATEWAY, SPLIT_TASK, TIMER, or SIGNALING
	GatewayType    GatewayType       `json:"gateway_type,omitempty"`     // EXCLUSIVE_SPLIT, PARALLEL_SPLIT, BATCH_SPLIT, BATCH_JOIN, etc.
	TaskTemplateID string            `json:"task_template_id,omitempty"` // ID of the task template to run
	InputMapping   map[string]string `json:"input_mapping,omitempty"`    // Maps WorkflowVariables -> Task Input Key
	OutputMapping  map[string]string `json:"output_mapping,omitempty"`   // Maps Task Output -> WorkflowVariables Key
	SplitTask      *SplitTaskConfig  `json:"split_task,omitempty"`       // Configuration for dynamic fan-out splits
	Timer          *TimerConfig      `json:"timer,omitempty"`            // Configuration for durable timer waits
	Signaling      *SignalingConfig  `json:"signaling,omitempty"`        // Configuration for EMIT/WAIT signaling
	BatchGateway   *BatchGatewayConfig `json:"batch_gateway,omitempty"`  // Configuration for BATCH_SPLIT item partitioning
	BatchJoin      *BatchJoinConfig    `json:"batch_join,omitempty"`     // Configuration for BATCH_JOIN item merging
}
Batch Gateway Configuration (BatchGatewayConfig & BatchJoinConfig)
type BatchGatewayConfig struct {
	ItemsVariable string `json:"items_variable,omitempty"` // Dot-path to []Item (defaults to "_items")
	IDField       string `json:"id_field,omitempty"`       // Unique item identifier field (defaults to "id")
}

type BatchJoinConfig struct {
	GatewayNodeID string `json:"gateway_node_id"`          // Node ID of paired BATCH_SPLIT
	ItemsVariable string `json:"items_variable,omitempty"` // Dot-path to []Item (defaults to "_items")
	IDField       string `json:"id_field,omitempty"`       // Unique item identifier field (defaults to "id")
}
What partitions see and hand back

Each partition runs as a child workflow, and the join merges its items back by ID. The BATCH_SPLIT node's own input_mapping and output_mapping control everything else:

{ "id": "treatment_split", "type": "GATEWAY", "gateway_type": "BATCH_SPLIT",
  "batch_gateway": { "items_variable": "commodities" },
  "input_mapping":  { "npqs.reference_number": "npqs.reference_number" },
  "output_mapping": { "treatment.certificate": "treatment.certificates" } }
  • input_mapping (parent path → child path): each partition starts with only the mapped variables, plus the engine's context variables (names starting with _, such as _case_id). A missing source parks the split unless its key ends in ?. A split without an input_mapping gives its partitions no workflow variables besides their items.
  • output_mapping (child path → parent path): the parent path is replaced with a flat list of the unique values the partitions hold at the child path, in partition order. A partition without the value contributes nothing, and a list value contributes its elements, so an outer split can gather what an inner split gathered. The list is empty when no partition contributes, including when no items reach the split. A trailing ? is accepted and changes nothing.
  • Neither mapping can name the items variable, an input_mapping cannot write a name starting with _, each output_mapping destination has one source, and mappings on a BATCH_JOIN are rejected.
Dynamic Fan-out Configuration (SplitTaskConfig)
type SplitTaskConfig struct {
	Mode            SplitMode   `json:"mode"`                       // SAME_TEMPLATE or DIFFERENT_TEMPLATES
	ItemsVariable   string      `json:"items_variable"`             // Global variables dot-path pointing to []map[string]any
	ResultsVariable string      `json:"results_variable,omitempty"` // Destination variable to save aggregated sub-workflow outputs
	FailureMode     FailureMode `json:"failure_mode"`               // FAIL_FAST or COLLECT_ALL
	IterationKey    string      `json:"iteration_key,omitempty"`    // Sub-context namespace key (defaults to "_iter")
}

Signaling Nodes

SIGNALING nodes with sub-type EMIT or WAIT let sibling branches spawned by the same SPLIT_TASK node coordinate with each other. The relay is one hop up, one hop back down — a child signals its immediate parent, and the parent rebroadcasts only to the other children it spawned for that same SPLIT_TASK node. It does not bubble further up an ancestor chain, and it does not cascade down into a sibling's own nested sub-splits. If you need coordination across more than one level of nesting, each level must explicitly re-emit the signal itself — there is no built-in multi-level relay.

EMIT sub-type

Broadcasts a signal from a child workflow to all sibling branches (every other child spawned by the same SPLIT_TASK node), brokered through the parent. Every sibling receives its own copy — there is no targeting a single branch.

  • Config fields: signal_name (required), payload (optional map[string]any).
  • Execution: Passes messages through the parent broker using a child_broadcast_signal:<split_node_id> channel, scoped to the SPLIT_TASK node that spawned this branch so concurrent split tasks in the same workflow don't cross-deliver broadcasts. Safe for standalone execution (gracefully warns if no parent workflow ID is registered).
{ "id": "emit_ready", "type": "SIGNALING", "signaling": { "type": "EMIT", "signal_name": "branch_ready", "payload": { "status": "ok" } } }
WAIT sub-type

Blocks workflow execution until one signal matching the specified channel name is received. Because EMIT broadcasts to every sibling, a branch with N−1 siblings will have N−1 signals queued after all siblings emit — one WAIT node consumes exactly one of them. Chain N−1 consecutive WAIT nodes to collect all of them (see the barrier pattern below).

  • Config fields: signal_name (required).
  • Output: Received payload is written into WorkflowVariables using the node's standard output_mapping.
{ "id": "wait_ready", "type": "SIGNALING", "signaling": { "type": "WAIT", "signal_name": "branch_ready" }, "output_mapping": { "status": "branch.status" } }
Signaling in SAME_TEMPLATE splits

When all branches run the same template, every branch has the same EMIT and WAIT nodes. Two patterns arise — one works cleanly, one deadlocks.

Barrier / rendezvous (✓ works) — each branch emits when it completes a phase, then collects one signal per sibling before proceeding. Since EMIT broadcasts to all siblings and Temporal queues the incoming signals, a sequence of N−1 consecutive WAIT nodes drains them one at a time:

Phase 1 → EMIT("checkpoint") → WAIT("checkpoint") → WAIT("checkpoint") → Phase 2
                                  ↑ signal from B        ↑ signal from C

Constraint: the number of WAIT nodes must be hardcoded to exactly N−1, so this pattern only works when the branch count is fixed and known at design time.

Deadlock (✗ avoid) — placing WAIT before EMIT on the same path means every branch blocks simultaneously and no branch ever reaches EMIT:

WAIT("checkpoint") → ... → EMIT("checkpoint")   ← every branch hangs here, forever

Recommended approach for producer/consumer coordination — use DIFFERENT_TEMPLATES so the asymmetry is explicit: one template contains only EMIT, the other contains only WAIT. There is no risk of accidental deadlock and the intent is clear from the DSL alone.


Integration Setup

To run the engine inside a Go application, initialize the TemporalManager with your task and completion handlers:

import engine "github.com/OpenNSW/core/workflow"

// Initialize the TemporalManager (this automatically registers the workflow and activities internally)
manager := engine.NewTemporalManager(
    temporalClient,
    "default",         // must match the namespace temporalClient was dialed with
    "your-task-queue",
    taskHandler,       // TaskActivationHandler
    completionHandler, // WorkflowCompletionHandler
)

// Register sub-workflow definition loader (required if using SPLIT_TASK nodes)
manager.RegisterDefinitionHandler(func(templateID string) (engine.WorkflowDefinition, error) {
    // Retrieve definition from database or local files
    return loadDefinition(templateID), nil
})

// Start the internal worker to begin execution
err := manager.StartWorker()
if err != nil {
    log.Fatalf("Failed to start worker: %v", err)
}

Running Tests

Run the integration suite locally to verify engine features:

go test -race -v ./...

Documentation

Overview

Package engine implements a Temporal-based graph interpreter workflow engine.

Index

Constants

View Source
const (
	// DefaultIterationKey is the default variable name injected into a child workflow's
	// state containing iteration details (e.g., _iter.index, _iter.branch_id, _iter.input).
	DefaultIterationKey = "_iter"
	// ChildBroadcastSignalName is the base name of the Temporal signal used to route cross-branch
	// signals from a child workflow back up to the parent for brokerage to other sibling branches.
	// It is scoped per SPLIT_TASK node via childBroadcastSignalName so that two SPLIT_TASK nodes
	// running concurrently in the same workflow execution (e.g. under a PARALLEL_SPLIT gateway)
	// don't share a channel and cross-deliver each other's broadcasts.
	ChildBroadcastSignalName = "child_broadcast_signal"

	// Keys injected into the child's workspace variables
	// VarSplitNodeID identifies the ID of the SplitTask node in the parent workflow.
	VarSplitNodeID = "_split_node_id"
	// VarParentWorkflowID contains the workflow ID of the parent/orchestrator workflow.
	VarParentWorkflowID = "_parent_workflow_id"
	// VarCaseID holds the case a workflow belongs to: the key the task store groups tasks by
	// (store.TaskRecord.CaseID) and the prefix of every child workflow ID. A host that runs
	// several top-level workflows for one real-world matter (a consignment, an agency case)
	// starts each with the same VarCaseID; a top-level workflow started without one is its own
	// case, under its own workflow ID. It is carried unchanged through every level of nesting,
	// so a descendant at any depth can read it directly without walking a parent-to-parent
	// chain.
	VarCaseID = "_case_id"
	// VarBranchID contains the unique branch ID assigned to the specific child workflow branch.
	VarBranchID = "_branch_id"

	// Iteration context sub-keys (e.g., used to access _iter.index, _iter.branch_id, _iter.input)
	// IterIndexKey is the sub-key for the 0-based index of this branch within the items array.
	IterIndexKey = "index"
	// IterBranchIDKey is the sub-key for the unique branch identifier.
	IterBranchIDKey = "branch_id"
	// IterInputKey is the sub-key pointing to the input payload mapped to this branch.
	IterInputKey = "input"
)

Core structural execution constants

View Source
const (
	DefaultItemsVariable = "_items"
	DefaultIDField       = "id"
)

Default values for BatchGatewayConfig / BatchJoinConfig fields.

View Source
const (
	// DefaultMaxBatchDepth is the maximum nesting depth of BATCH_SPLIT gateways.
	DefaultMaxBatchDepth = 4
	// DefaultMaxChildrenPerGateway is the maximum number of partitions a single
	// BATCH_SPLIT gateway may spawn.
	DefaultMaxChildrenPerGateway = 20
)

Engine-level guardrail defaults for batch gateways.

View Source
const AdminResolutionSignalName = "AdminResolutionSignal"

AdminResolutionSignalName is the Temporal signal name used to deliver AdminResolutionSignal.

View Source
const VarScopePath = "_scope_path"

VarScopePath is the workflow variable key holding the hierarchical scope path string for batch gateway nesting (e.g. "root/gw_type/lab/gw_result/fail").

Variables

View Source
var ErrActivationNotPending = errors.New("step is not pending: it already completed or does not exist")

ErrActivationNotPending is returned by Manager.CompleteActivation when the addressed step is not waiting to be completed: it already completed, or the workflow ended, or no such step ever existed. Nothing was changed. It is how a caller learns its call was late or a duplicate, for example one for an earlier run of a node the workflow has since moved past. Check for it with errors.Is.

View Source
var ErrWorkflowNotFound = errors.New("workflow execution not found")

ErrWorkflowNotFound is returned by Manager.GetStatus when the backing engine has no queryable execution for the given ID. This covers an ID that was never used, but also one whose execution is no longer queryable for any other reason (e.g. its history has aged out of the engine's retention) — a Manager implementation cannot always tell the two apart. Callers should check for ErrWorkflowNotFound with errors.Is rather than inspecting the underlying runtime's error types, so they stay agnostic to whatever engine backs the Manager.

Functions

func EvaluateCondition

func EvaluateCondition(condition string, context map[string]any) (bool, error)

EvaluateCondition parses and runs a string expression against the global context.

func FormatChildWorkflowID

func FormatChildWorkflowID(caseID, parentWorkflowID, nodeID, branchID string) string

FormatChildWorkflowID constructs a deterministic child workflow ID from the case ID, the parent's own workflow ID, the gateway node ID, and the branch ID.

The result is always "<root>--<16-hex-char hash>": each level's hash folds in the parent's full workflow ID, not just this level's nodeID/branchID, so the ID uniquely commits to the full ancestor chain no matter how deep the nesting goes. Fixed-width output keeps the ID within Temporal's varchar(255) current_executions.workflow_id column regardless of branch depth — an oversized case ID is capped via boundPrefixForChildID first, so the guarantee holds independent of how long the caller's case ID is. Two workflows of the same case (a host's several top-level workflows) share the prefix; the hash, which folds in the parent's workflow ID, keeps their children apart.

caseID is taken as given rather than parsed out of parentWorkflowID — callers already have it via VarCaseID, which the engine propagates explicitly to every level of nesting.

func ValidateBatchGateways

func ValidateBatchGateways(def WorkflowDefinition) error

ValidateBatchGateways checks structural invariants and topological containment for BATCH_SPLIT / BATCH_JOIN pairs in the workflow definition. Call this at parse-time or at the start of GraphInterpreterWorkflow before execution begins.

func ValidateParallelGateways

func ValidateParallelGateways(def WorkflowDefinition) error

ValidateParallelGateways checks structural invariants and topological containment for PARALLEL_SPLIT / PARALLEL_JOIN pairs in the workflow definition, mirroring ValidateBatchGateways for BATCH_SPLIT / BATCH_JOIN. Call this at parse-time or at the start of GraphInterpreterWorkflow before execution begins.

Types

type ActivationRef

type ActivationRef struct {
	// NodeID is the node's ID in the workflow definition (Node.ID).
	NodeID string `json:"node_id"`
	// Seq is the workflow-wide step counter value for this run.
	Seq int64 `json:"seq"`
}

ActivationRef is what the workflow tells ExecuteTaskActivity about the run it is starting, beyond what the Activity's own info carries.

type Activities

type Activities struct {
	// ExecuteTaskActivityHandler is invoked when the workflow engine encounters a task node.
	// - For synchronous execution, it should return a nil error with a map containing the results.
	// - For asynchronous execution, it should return a nil map and an ErrResultPending error,
	//   which pauses the workflow activity until an external handler triggers CompleteActivation.
	ExecuteTaskActivityHandler func(TaskPayload) (map[string]any, error)

	// WorkflowCompletedActivityHandler is invoked when the overall workflow execution succeeds and reaches
	// an End node. It receives the workflow ID and the final accumulated workflow variables, allowing the
	// host application to run any necessary completion triggers, notify listeners, or persist final state.
	WorkflowCompletedActivityHandler func(WorkflowCompletion) error

	// FetchWorkflowDefinitionHandler is invoked to dynamically retrieve the workflow definition structure
	// for a given template ID during SPLIT_TASK execution.
	FetchWorkflowDefinitionHandler func(templateID string) (WorkflowDefinition, error)

	// AdminParkHandler is invoked whenever a node parks for admin intervention. See the
	// AdminParkHandler type doc for what a nil handler means.
	AdminParkHandler AdminParkHandler
}

Activities encapsulates the Temporal activity implementations utilized by the workflow engine. It maps the activity execution flow to custom callback handlers provided by the host application.

func (*Activities) AdminParkActivity

func (a *Activities) AdminParkActivity(_ context.Context, payload AdminParkPayload) error

AdminParkActivity is a Temporal activity that notifies the host application whenever a node parks for admin intervention. It is a no-op if no AdminParkHandler is registered — unlike the other handlers here, notification is optional, so an unset handler is not an error.

func (*Activities) ExecuteTaskActivity

func (a *Activities) ExecuteTaskActivity(ctx context.Context, taskTemplateID string, inputs map[string]any, caseID string, step ActivationRef) (map[string]any, error)

ExecuteTaskActivity pushes the task to your application and sleeps waiting for it or completes synchronously

func (*Activities) FetchWorkflowDefinitionActivity

func (a *Activities) FetchWorkflowDefinitionActivity(_ context.Context, templateID string) (WorkflowDefinition, error)

FetchWorkflowDefinitionActivity is a Temporal activity that retrieves the workflow definition for a template ID.

func (*Activities) WorkflowCompletedActivity

func (a *Activities) WorkflowCompletedActivity(_ context.Context, workflowID string, finalContext map[string]any, seq int64) error

WorkflowCompletedActivity is a Temporal activity that executes when a workflow completes successfully.

type AdminInterventionResolver

type AdminInterventionResolver interface {
	// ResolveAdminIntervention sends an admin's resolution decision to a node that is
	// currently parked in NodeStatusAwaitingAdmin. workflowID/runID address the execution, and
	// resolution.ActivationID picks the parking within it: it must echo AdminParkPayload.ActivationID
	// (or NodeInfo.ActivationID from GetStatus) for the current park, or the signal is dropped.
	// resolution.NodeID is for display only. A nil return means the signal was delivered, not that
	// it matched a parked node.
	ResolveAdminIntervention(ctx context.Context, workflowID, runID string, resolution AdminResolutionSignal) error
}

AdminInterventionResolver is implemented by managers that support resolving a node parked in NodeStatusAwaitingAdmin. It is kept separate from Manager so that existing Manager implementations (e.g. test doubles) aren't forced to implement it. Callers that want this capability can type-assert: if r, ok := mgr.(AdminInterventionResolver); ok { ... }.

type AdminParkHandler

type AdminParkHandler func(AdminParkPayload) error

AdminParkHandler is invoked once every time a node parks in NodeStatusAwaitingAdmin — including a re-park after a failed AdminActionRetry, since that's newly actionable information. It hands the host application full control over how to surface the event (log, metric, DB write, page, etc.); the engine itself does not prescribe a mechanism. Registering one is optional — a nil handler (the default) means the engine takes no notification action at all, and parking still proceeds correctly either way. A handler error is recorded on the workflow's own AuditTrail but never blocks the park or the admin resolution flow.

type AdminParkPayload

type AdminParkPayload struct {
	// WorkflowID is the unique identifier for the workflow execution that parked.
	WorkflowID string
	// RunID is the unique identifier for this specific execution attempt.
	RunID string
	// CaseID is the case the parked workflow belongs to (see VarCaseID).
	CaseID string
	// NodeID is the ID of the graph node that parked. It names the definition, not this one run of
	// it; see ActivationID for a run.
	NodeID string
	// ActivationID identifies this specific run of the node — the same value NodeInfo.ActivationID
	// held at the moment it parked. A resolution must echo it back (AdminResolutionSignal.ActivationID)
	// to be matched to this parking rather than some other one, including an earlier parking of the
	// same NodeID (a retry or loop revisit mints a new ActivationID each time it parks).
	ActivationID string
	// NodeType is the type of the node that parked (TASK, GATEWAY, etc.).
	NodeType string
	// TaskTemplateID identifies the task template the parked node was running. Empty for
	// non-TASK nodes (e.g. a GATEWAY parked on "no matching conditions").
	TaskTemplateID string
	// Cause is the error message that caused the node to park (NodeInfo.LastError).
	Cause string
	// ParkCategory says why the node parked (see NodeInfo.ParkCategory).
	ParkCategory ParkCategory
	// InputMapping and OutputMapping are the node's mappings from its definition, carried over
	// from NodeInfo.InputMapping/OutputMapping so a handler can report which workflow variable a
	// RETRY reads and which a COMPLETE patch should write, without querying the workflow itself.
	InputMapping  map[string]string
	OutputMapping map[string]string
	// CachedTaskResult holds the Activity's raw result if it had already completed before the
	// node parked (see NodeInfo.CachedTaskResult) — nil if execution never reached that point.
	CachedTaskResult map[string]any
}

AdminParkPayload carries the context of a node parking in NodeStatusAwaitingAdmin, delivered to an AdminParkHandler so the host application can decide how to surface the event — log it, emit a metric, persist it, page someone, etc. The engine itself takes no position on that; see AdminParkHandler.

type AdminResolutionAction

type AdminResolutionAction string

AdminResolutionAction describes how an admin chooses to resolve a node that is parked in NodeStatusAwaitingAdmin.

const (
	// AdminActionRetry applies WorkflowVariablesPatch, then re-runs the node's handler from
	// scratch. It is the admin's responsibility to ensure this is safe — e.g. a TASK node with a
	// populated CachedTaskResult has already run its Activity, so retrying re-invokes it.
	AdminActionRetry AdminResolutionAction = "RETRY"
	// AdminActionComplete applies WorkflowVariablesPatch and marks the node completed without
	// running it (or re-running it): the patch stands in for the node's output. An empty patch
	// just moves past the node. Always safe, but not allowed for GATEWAY nodes.
	AdminActionComplete AdminResolutionAction = "COMPLETE"
	// AdminActionAbort fails the node and the workflow with the original error.
	AdminActionAbort AdminResolutionAction = "ABORT"
)

Admin resolution actions.

type AdminResolutionSignal

type AdminResolutionSignal struct {
	// NodeID is the template node ID (Node.ID) of the parked node, for display/logging. It is not
	// the routing key: ActivationID is.
	NodeID string `json:"nodeID"`
	// ActivationID is the routing key: it must match the ActivationID the engine sent in
	// AdminParkPayload for the parking being resolved. A signal whose ActivationID doesn't match any
	// currently-parked node is dropped — including one echoing an earlier parking of the same
	// NodeID, since a retry or loop revisit mints a new ActivationID each time it parks. Its JSON
	// name, step_id, matches NodeInfo.ActivationID's, so a value read from GetStatus is sent back
	// under the same name.
	ActivationID string `json:"step_id"`
	// Action determines how the node is resolved. See AdminResolutionAction constants.
	Action AdminResolutionAction `json:"action"`
	// WorkflowVariablesPatch sets workflow variables before the action takes effect, for
	// AdminActionRetry and AdminActionComplete. Keys are dotted paths (e.g. "review.outcome"),
	// applied in sorted order, and each value is written the way a task's output mapping writes
	// one: a map value is merged into an existing map at that path (fields it doesn't name are
	// kept), and any other value replaces what is there. It is a patch, not the full variable set
	// — variables not named here are untouched. It is not an RFC 6902 JSON Patch, and a nil value
	// sets the path to nil rather than deleting it. To drop a map's other fields, send it as nil
	// alongside the fields to keep: {"review": nil, "review.outcome": true}. The variables are
	// workflow-wide and persist, so they affect every later node too.
	WorkflowVariablesPatch map[string]any `json:"workflow_variables_patch,omitempty"`
	// Reason is a free-text admin justification, appended to the workflow's AuditTrail.
	Reason string `json:"reason,omitempty"`
}

AdminResolutionSignal is sent by an external admin tool to resolve a node that is parked in NodeStatusAwaitingAdmin.

type BatchGatewayConfig

type BatchGatewayConfig struct {
	// ItemsVariable is the dot-path in WorkflowVariables pointing to the item slice.
	// Defaults to "_items" if empty.
	ItemsVariable string `json:"items_variable,omitempty"`

	// IDField is the field name within each item used as the unique identifier for
	// ID-based merging at the paired BATCH_JOIN. Defaults to "id" if empty.
	IDField string `json:"id_field,omitempty"`
}

BatchGatewayConfig configures item-level partitioning for a BATCH_SPLIT gateway node.

type BatchJoinConfig

type BatchJoinConfig struct {
	// GatewayNodeID is the node ID of the paired BATCH_SPLIT gateway.
	GatewayNodeID string `json:"gateway_node_id"`

	// ItemsVariable is the dot-path in WorkflowVariables pointing to the item slice.
	// Must match the paired BATCH_SPLIT's items_variable. Defaults to "_items" if empty.
	ItemsVariable string `json:"items_variable,omitempty"`

	// IDField is the field name within each item used as the unique identifier.
	// Must match the paired BATCH_SPLIT's id_field. Defaults to "id" if empty.
	IDField string `json:"id_field,omitempty"`
}

BatchJoinConfig configures how a BATCH_JOIN gateway merges child partition results.

type BroadcastMessage

type BroadcastMessage struct {
	SenderBranchID string         `json:"sender_branch_id"`
	SignalName     string         `json:"signal_name"`
	Payload        map[string]any `json:"payload"`
}

BroadcastMessage defines a unified Message Wrapper for parent brokerage.

type Edge

type Edge struct {
	ID        string `json:"id"`
	SourceID  string `json:"source_id"`
	TargetID  string `json:"target_id"`
	Condition string `json:"condition,omitempty"` // Expression mapped against WorkflowVariables
}

Edge represents a directed connection between two nodes.

type ExecutionStatus

type ExecutionStatus string

ExecutionStatus defines the allowed states for a workflow instance.

const (
	StatusRunning   ExecutionStatus = "RUNNING"
	StatusCompleted ExecutionStatus = "COMPLETED"
	StatusFailed    ExecutionStatus = "FAILED"
)

Execution status constants for workflows.

type FailureMode

type FailureMode string

FailureMode represents the failure handling strategy for split task executions.

const (
	FailureModeFailFast   FailureMode = "FAIL_FAST"
	FailureModeCollectAll FailureMode = "COLLECT_ALL"
)

Core split task failure handling modes.

type GatewayType

type GatewayType string

GatewayType represents the type of a gateway controlling execution flow.

const (
	GatewayTypeExclusiveSplit GatewayType = "EXCLUSIVE_SPLIT" // XOR Split
	GatewayTypeParallelSplit  GatewayType = "PARALLEL_SPLIT"  // AND Split
	GatewayTypeExclusiveJoin  GatewayType = "EXCLUSIVE_JOIN"  // XOR Join
	GatewayTypeParallelJoin   GatewayType = "PARALLEL_JOIN"   // AND Join
	GatewayTypeBatchSplit     GatewayType = "BATCH_SPLIT"     // Item-partitioning Split
	GatewayTypeBatchJoin      GatewayType = "BATCH_JOIN"      // Item-merging Join
)

Gateway types controlling branching and merging.

type Manager

type Manager interface {
	// StartWorkflow starts a workflow using the provided ID.
	// The WorkflowDefinition defines the structure of the workflow graph (nodes and edges).
	// initialWorkflowVariables sets the starting state for the graph's
	// data payload. Returns an error if submission fails.
	StartWorkflow(ctx context.Context, ID string, def WorkflowDefinition, initialWorkflowVariables map[string]any) error

	// CompleteActivation is called by the external system to resume a paused workflow node.
	// It routes the output data back into the specific workflow's WorkflowVariables using the
	// workflowID and activationID (TaskPayload.ActivationID) that were originally emitted via the
	// TaskActivationHandler. runID may be empty to address the workflow's current run. It fails if
	// that step is not pending, so a call for a step that has already completed, or that never
	// existed, is rejected rather than applied to whichever step is active now.
	CompleteActivation(ctx context.Context, workflowID, runID, activationID string, output map[string]any) error

	// TaskUpdate is used to send an update about the task to the workflow.
	// This is typically used to append messages to the workflow's internal state or update
	// UI with hints, progress updates, or audit trail messages.
	// It does not advance the graph's execution state.
	TaskUpdate(ctx context.Context, workflowID, runID string, update UpdateEvent) error

	// GetStatus retrieves a running workflow's in-memory state (the WorkflowInstance), including
	// current variables, and audit trails. Returns ErrWorkflowNotFound if there is no queryable
	// current execution for workflowID — see ErrWorkflowNotFound's doc for what that covers.
	GetStatus(ctx context.Context, workflowID string) (*WorkflowInstance, error)
}

Manager acts as the bridge between the external host application and the underlying execution engine. It handles workflow lifecycles, external task routing, and state queries.

type Node

type Node struct {
	ID             string            `json:"id"`
	Type           NodeType          `json:"type"`                       // See NodeType constants
	GatewayType    GatewayType       `json:"gateway_type,omitempty"`     // See Gateway Types constants
	TaskTemplateID string            `json:"task_template_id,omitempty"` // Identifier for the task template to run
	InputMapping   map[string]string `json:"input_mapping,omitempty"`    // Maps WorkflowVariables Key -> Task Input Key
	OutputMapping  map[string]string `json:"output_mapping,omitempty"`   // Maps Task Output Key -> WorkflowVariables Key

	// Extensions
	SplitTask    *SplitTaskConfig    `json:"split_task,omitempty"`
	Timer        *TimerConfig        `json:"timer,omitempty"`
	Signaling    *SignalingConfig    `json:"signaling,omitempty"`
	BatchGateway *BatchGatewayConfig `json:"batch_gateway,omitempty"`
	BatchJoin    *BatchJoinConfig    `json:"batch_join,omitempty"`
	ParallelJoin *ParallelJoinConfig `json:"parallel_join,omitempty"`
}

Node represents a step in the workflow graph.

type NodeInfo

type NodeInfo struct {
	// ID is the node's ID in the workflow definition (Node.ID). It names the definition, not one
	// run of it; see ActivationID for a run.
	ID             string      `json:"id"`
	CreatedAt      time.Time   `json:"createdAt"`                  // Timestamp of node creation
	UpdatedAt      time.Time   `json:"updatedAt"`                  // Timestamp of last node update
	Type           NodeType    `json:"type"`                       // Type of the node.
	GatewayType    GatewayType `json:"gateway_type,omitempty"`     // See Gateway Types constants
	TaskTemplateID string      `json:"task_template_id,omitempty"` // Identifier for the task template to run
	Status         NodeStatus  `json:"status"`                     // Status of the node

	// ActivationID and Seq identify the most recent run of this node, of any type (see
	// TaskPayload.ActivationID and TaskPayload.Seq, which name the same values for a TASK node
	// specifically). Empty and zero until the node first runs. A node that is revisited gets a new
	// ActivationID and a higher Seq each time; these always describe the latest run.
	ActivationID string `json:"step_id,omitempty"`
	Seq          int64  `json:"seq,omitempty"`

	// LastError holds the error that caused the node to enter NodeStatusAwaitingAdmin
	// (or the terminal error if it was ultimately aborted). Cleared on successful resolution.
	LastError string `json:"last_error,omitempty"`
	// ParkCategory says why the node is parked. Set together with LastError; cleared on resolution.
	ParkCategory ParkCategory `json:"park_category,omitempty"`
	// InputMapping and OutputMapping are the node's mappings from its definition (workflow variable
	// -> task input, task result -> workflow variable), so an admin can see which variables a
	// RETRY reads and which a COMPLETE should write. Set together with LastError, only while the
	// node is parked (not on every node); cleared on resolution.
	InputMapping  map[string]string `json:"input_mapping,omitempty"`
	OutputMapping map[string]string `json:"output_mapping,omitempty"`
	// CachedTaskResult holds the most recent raw Activity result for a TASK node, set right
	// after the Activity succeeds and cleared once the node fully completes. It is purely
	// informational: if a node parks with this populated, the Activity has already run, so
	// an admin should prefer AdminActionComplete over AdminActionRetry to avoid re-running it.
	CachedTaskResult map[string]any `json:"cached_task_result,omitempty"`

	// ChildWorkflowIDs lists the workflow IDs of any child GraphInterpreterWorkflow executions
	// spawned by this node (SPLIT_TASK, BATCH_SPLIT, or PARALLEL_SPLIT). It is set once, at
	// spawn time, and is not derived from the children's current status — so an ID remains
	// listed here even after that child workflow completes. A caller can query each ID with the
	// same GetStatus query used on this workflow to walk the execution tree to arbitrary depth.
	ChildWorkflowIDs []string `json:"child_workflow_ids,omitempty"`
}

NodeInfo holds information about the state of one of the nodes in the workflow.

type NodeStatus

type NodeStatus string

NodeStatus represents the status of a specific workflow node.

const (
	NodeStatusNotStarted    NodeStatus = "NOT_STARTED"
	NodeStatusRunning       NodeStatus = "RUNNING"
	NodeStatusCompleted     NodeStatus = "COMPLETED"
	NodeStatusFailed        NodeStatus = "FAILED"
	NodeStatusAwaitingAdmin NodeStatus = "AWAITING_ADMIN"
)

Node status constants.

type NodeType

type NodeType string

NodeType represents the type of a workflow node (e.g. START, END, TASK, GATEWAY).

const (
	NodeTypeStart     NodeType = "START"
	NodeTypeEnd       NodeType = "END"
	NodeTypeTask      NodeType = "TASK"
	NodeTypeGateway   NodeType = "GATEWAY"
	NodeTypeSplitTask NodeType = "SPLIT_TASK"
	NodeTypeTimer     NodeType = "TIMER"
	NodeTypeSignaling NodeType = "SIGNALING"
)

Core node types supported by the engine.

type ParallelJoinConfig

type ParallelJoinConfig struct {
	// GatewayNodeID is the node ID of the paired PARALLEL_SPLIT gateway.
	GatewayNodeID string `json:"gateway_node_id"`

	// MergeByID maps a top-level workflow variable name (holding a []map[string]any shared by
	// multiple branches) to the field name within each item used as its unique ID.
	// Nested dot-paths are not currently supported; see ValidateParallelGateways.
	// TODO: Support nested dot-paths (e.g. "order.items") in MergeByID and mergeVariablesInto.
	MergeByID map[string]string `json:"merge_by_id,omitempty"`
}

ParallelJoinConfig configures how a PARALLEL_JOIN gateway isolates its branches and merges their final workflow variable states back into the parent scope.

WARNING: CONFLICT RESOLUTION AND LAST-WRITE-WINS (LWW) When concurrent parallel branches write conflicting data, the engine cannot infer intent and applies a deterministic "last-write-wins" policy. Precedence is determined strictly by branch edge order (sorted ascending by source edge ID; later branches in that order overwrite earlier ones), NOT wall-clock completion time.

Specifically, LAST-WRITE-WINS applies in the following three places:

  1. Conflicting item fields in MergeByID: If two branches modify the SAME field on the SAME item (e.g. branch A sets item["status"]="PASS" and branch B sets item["status"]="FAIL"), the later branch's field value overwrites the earlier one.
  2. Conflicting map keys: If two branches set the SAME leaf key in a map[string]any, the later branch's value overwrites the earlier one.
  3. Scalars and unlisted arrays: Any scalar variable (string, number, boolean) or slice not listed in MergeByID that is written by multiple branches will be completely overwritten by the later branch.

Recommendation for workflow authors: Ensure concurrent parallel branches write to distinct variable paths or distinct field names on shared items if overwriting cannot be tolerated.

Execution and Reconciliation Model: Each matching outgoing edge of the paired PARALLEL_SPLIT runs as its own child workflow with a deep-copied, isolated set of WorkflowVariables — no branch can see another branch's writes while any of them are still running. When all branches complete, PARALLEL_JOIN reconciles their (possibly divergent) final states back into one:

  • map[string]any values are deep-merged key by key (recursive map merge): each branch's mutated sub-fields combine, so two branches changing different fields of the same object both survive.
  • Variables listed in MergeByID (each a []map[string]any) are merged element-by-element by that id_field across ALL branches — not "one branch's array replaces another's": for a given item ID, the fields each branch's copy of that item carries are unioned into one item, so lab writing sample_test_result and visual writing visual_result to the same underlying item both survive regardless of which branch finishes last. A field two branches BOTH carry a (possibly different) value for is a genuine conflict this cannot resolve: branches are applied in a fixed, deterministic order (sorted by source edge ID) and the last one wins for that field — give each branch its own field name if that ambiguity isn't acceptable.
  • Any other variable (scalar, or array not listed in MergeByID) falls back to the same fixed deterministic branch order, last one wins.

type ParkCategory

type ParkCategory string

ParkCategory says why a node parked in NodeStatusAwaitingAdmin, so tooling can guide an admin without parsing NodeInfo.LastError.

const (
	// ParkCategoryInputMapping: a required global variable for the node's input mapping is missing.
	ParkCategoryInputMapping ParkCategory = "INPUT_MAPPING"
	// ParkCategoryOutputMapping: the Activity ran, but its result lacks a required mapped field.
	ParkCategoryOutputMapping ParkCategory = "OUTPUT_MAPPING"
	// ParkCategoryTaskFailure: the node's own work (the Activity, a signal send) failed.
	ParkCategoryTaskFailure ParkCategory = "TASK_FAILURE"
	// ParkCategoryGatewayCondition: a gateway condition failed to evaluate, or no edge matched.
	ParkCategoryGatewayCondition ParkCategory = "GATEWAY_CONDITION"
	// ParkCategorySplitData: the items or branch data a split or join needs is missing or malformed.
	ParkCategorySplitData ParkCategory = "SPLIT_DATA"
	// ParkCategoryChildFailure: a spawned child workflow failed.
	ParkCategoryChildFailure ParkCategory = "CHILD_FAILURE"
	// ParkCategoryDefinitionError: the workflow definition itself is invalid, so no variable fixes it.
	ParkCategoryDefinitionError ParkCategory = "DEFINITION_ERROR"
	// ParkCategoryUnknown: the error was never categorized.
	ParkCategoryUnknown ParkCategory = "UNKNOWN"
)

type SignalingConfig

type SignalingConfig struct {
	// Type is either EMIT or WAIT.
	Type SignalingType `json:"type"`
	// SignalName is the name of the signal channel. Required.
	SignalName string `json:"signal_name"`
	// Payload holds the data to emit (EMIT nodes only). Ignored for WAIT nodes.
	Payload map[string]any `json:"payload,omitempty"`
}

SignalingConfig defines the parameters for a SIGNALING node. SIGNALING nodes let sibling branches spawned by the same SPLIT_TASK node coordinate with each other: one hop up to the parent, one hop back down to that node's other children. They do not bubble further up an ancestor chain or cascade down into a sibling's own nested sub-splits — see the "Signaling Nodes" section in README.md.

type SignalingType

type SignalingType string

SignalingType represents the sub-type of a SIGNALING node.

const (
	SignalingTypeEmit SignalingType = "EMIT" // Emits a signal to sibling branches
	SignalingTypeWait SignalingType = "WAIT" // Blocks until a named signal is received
)

Signaling sub-types.

type SplitMode

type SplitMode string

SplitMode represents the split task dynamic fan-out mode.

const (
	// SplitModeSameTemplate spawns one child workflow per item running the same external template.
	//
	// Deprecated: Prefer using BATCH_SPLIT gateway nodes instead. BATCH_SPLIT defines sub-graphs
	// inline, supports conditional partitioning, aggregates tasks over item slices, and merges results
	// by item ID without requiring external template registration.
	SplitModeSameTemplate SplitMode = "SAME_TEMPLATE"

	// SplitModeDifferentTemplates spawns child workflows dynamically based on each item's template_id.
	SplitModeDifferentTemplates SplitMode = "DIFFERENT_TEMPLATES"
)

Core split task execution modes.

type SplitTaskConfig

type SplitTaskConfig struct {
	Mode            SplitMode   `json:"mode"`                       // SAME_TEMPLATE or DIFFERENT_TEMPLATES
	ItemsVariable   string      `json:"items_variable"`             // Global context variable dot-path pointing to []map[string]any
	ResultsVariable string      `json:"results_variable,omitempty"` // Destination path to save aggregated sub-workflow outputs
	FailureMode     FailureMode `json:"failure_mode"`               // FAIL_FAST or COLLECT_ALL
	IterationKey    string      `json:"iteration_key,omitempty"`    // Override key for sub-context namespace. Defaults to "_iter"
}

SplitTaskConfig defines dynamic fan-out execution configuration.

type SplitTaskItem

type SplitTaskItem struct {
	TemplateID string         `json:"template_id"`
	BranchID   string         `json:"branch_id"`
	Payload    map[string]any `json:"payload"`
}

SplitTaskItem defines the structure for individual branch items inside the items collection.

func ParseSplitTaskItem

func ParseSplitTaskItem(itemRaw any) (SplitTaskItem, error)

ParseSplitTaskItem parses a raw interface item into a SplitTaskItem.

type TaskActivationHandler

type TaskActivationHandler func(payload TaskPayload) (map[string]any, error)

TaskActivationHandler is invoked by the engine whenever the workflow reaches a "TASK" node. The handler must support two execution paths:

1. Synchronous Execution:

  • If the work completes immediately, return a nil error and a map containing the output variables.
  • The workflow immediately consumes these outputs and proceeds to the next node.

2. Asynchronous Execution:

  • If the work is long-running (e.g. awaits external API callback, human UI interaction, etc.), return a nil map and an ErrResultPending error.
  • The workflow activity pauses and awaits completion. The host application must eventually resume it by calling Manager.CompleteActivation() with the matching workflow, run, and step IDs.

type TaskPayload

type TaskPayload struct {
	// WorkflowID is the unique identifier for the overall business process instance.
	WorkflowID string
	// RunID is the unique identifier for this specific execution attempt.
	RunID string
	// NodeID is the ID of the graph node in the workflow definition (Node.ID). It names the
	// definition, not one run: a node revisited by a loop has the same NodeID each time. Use
	// ActivationID to address one run.
	NodeID string
	// ActivationID identifies this run of the node. It is unique within the workflow, is the ID of the
	// Temporal Activity, and is what Manager.CompleteActivation takes to complete this run. It is derived
	// deterministically from the workflow ID, NodeID and Seq (a UUIDv5), so it is a valid UUID.
	ActivationID string
	// Seq is the workflow-wide step counter value for this run. Every node run (not just TASK)
	// consumes the next value, so a TASK node's Seq need not be contiguous with the previous TASK
	// node's, but it never repeats or goes backwards. Hosts use it to order writes so a late write
	// from an earlier step can be told apart from the current one.
	Seq int64
	// TaskTemplateID identifies the specific type of external work/script the task executor should run.
	TaskTemplateID string
	// Inputs contains the specific subset of WorkflowVariables mapped to this task's requirements.
	Inputs map[string]any
	// CaseID is the case the workflow belongs to (see VarCaseID). It is the same at every level
	// of nesting, so it names the case of a task in a deeply nested child, which WorkflowID
	// alone does not.
	CaseID string
}

TaskPayload represents the contextual data sent to the task executor when the workflow engine reaches a "TASK" node. It contains the necessary coordinates for the task executor to identify the work and eventually report back.

type TemporalManager

type TemporalManager interface {
	Manager

	// RegisterDefinitionHandler registers the handler function for fetching sub-workflow definitions.
	RegisterDefinitionHandler(handler func(templateID string) (WorkflowDefinition, error))

	// RegisterAdminParkHandler registers the handler invoked whenever a node parks for admin
	// intervention. Optional — see AdminParkHandler's doc for what a nil/unregistered handler
	// means.
	RegisterAdminParkHandler(handler AdminParkHandler)

	// StartWorker connects the internal Temporal Worker to the Temporal Server and
	// begins polling the task queue for workflow and activity tasks.
	StartWorker() error

	// StopWorker gracefully shuts down the internal Temporal Worker, stopping it from
	// pulling new tasks while allowing currently executing tasks to finish.
	StopWorker()
}

TemporalManager extends the Manager interface with worker control methods.

func NewTemporalManager

func NewTemporalManager(
	c client.Client,
	namespace string,
	taskQueue string,
	taskHandler TaskActivationHandler,
	completionHandler WorkflowCompletionHandler) TemporalManager

NewTemporalManager creates a new instance of TemporalManager.

namespace must be the namespace c was dialed with. client.Client does not expose it, and some calls (e.g. CompleteActivityByID) require it explicitly.

type TimerConfig

type TimerConfig struct {
	// Duration is how long to wait, as a Go duration string ("30s", "1m", "2h").
	// Required, and must be positive.
	Duration string `json:"duration"`

	// CounterKey is the workflow-variable dot-path the node writes its fire
	// count to, so a downstream gateway can bound a polling loop (for example
	// "status.poll_attempts" against a condition of "< 60"). The count is
	// written before the wait begins and starts at 1. Defaults to
	// "<node id>.iterations".
	CounterKey string `json:"counter_key,omitempty"`
}

TimerConfig defines how long a TIMER node waits before following its single outgoing edge. The wait is a durable Temporal timer, so it survives worker restarts and holds no activity or worker slot while it runs.

type UpdateEvent

type UpdateEvent struct {
	// EventType categorizes the signal (e.g., "AUDIT", "PROGRESS_UPDATE", "UI_HINT")
	// so the workflow knows how to route the data internally.
	EventType string `json:"eventType"`
	// NodeID is the ID of the graph node being updated.
	NodeID string `json:"nodeID"`
	// Payload contains the contextual data for the event (e.g., percentage complete, or audit text).
	Payload map[string]any `json:"payload,omitempty"`
}

UpdateEvent allows the task executor to send asynchronous signals into a running workflow (e.g., while a task is active).

type WorkflowCompletion

type WorkflowCompletion struct {
	// WorkflowID is the ID of the workflow that completed.
	WorkflowID string
	// Seq is the workflow's step counter taken for the END node's own run, so higher than every
	// other node's Seq in this workflow. A host that orders its writes by Seq uses it to write the
	// completion so a late write from any earlier step matches nothing.
	Seq int64
	// FinalVariables is the workflow's accumulated variables.
	FinalVariables map[string]any
}

WorkflowCompletion is what the engine reports when a top-level workflow reaches its END node.

type WorkflowCompletionHandler

type WorkflowCompletionHandler func(WorkflowCompletion) error

WorkflowCompletionHandler is invoked when the generic DAG workflow successfully reaches an "End" node, providing the final, accumulated state of the workflow variables. It may be invoked more than once for the same workflow (Temporal retries the Activity), so it must be idempotent.

type WorkflowDefinition

type WorkflowDefinition struct {
	// ID is the unique identifier for this specific workflow template.
	ID string `json:"id"`

	// Name is a human-readable label used for display and organizational purposes.
	Name string `json:"name"`

	// Version tracks iterations of the workflow logic, allowing for side-by-side
	// deployment of different logic versions.
	Version int `json:"version"`

	// Nodes defines the individual steps, gateways, and boundary events
	// that make up the workflow.
	Nodes []Node `json:"nodes"`

	// Edges defines the directed connections between nodes, including
	// any conditional logic required for branching.
	Edges []Edge `json:"edges"`
}

WorkflowDefinition represents the structural blueprint of a workflow process. It serves as the parsed representation of the JSON DSL, defining how nodes and edges form a directed graph for the execution engine.

type WorkflowInstance

type WorkflowInstance struct {
	// ID is the unique ID for this instance of the workflow.
	ID string `json:"id"`
	// Status represents the current execution state.
	Status ExecutionStatus `json:"status"`
	// WorkflowVariables holds the shared, dynamic business data passed between nodes.
	WorkflowVariables map[string]any `json:"workflow_variables"`
	// AuditTrail is a chronologically ordered log of events, milestones, or external signals.
	AuditTrail []string             `json:"audit_trail"`
	NodeInfo   map[string]*NodeInfo `json:"node_states"`
	// Edges contains the workflow graph connections from the workflow definition.
	Edges []Edge `json:"edges"`
}

WorkflowInstance holds the dynamic runtime state of the workflow execution. This struct is returned by the GetStatus query and represents a deterministic snapshot of the engine's memory at a given point in time.

func GraphInterpreterWorkflow

func GraphInterpreterWorkflow(ctx workflow.Context, def WorkflowDefinition, initialWorkflowVariables map[string]any) (*WorkflowInstance, error)

GraphInterpreterWorkflow is the entry point for the Temporal workflow that interprets a graph definition.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL