Documentation
¶
Overview ¶
Package workflow is the domain for AI-orchestrated multi-step automations stored at `<BaseDir>/workflows/<id>/`. A workflow is a directed acyclic graph of typed nodes (classify/agent/connector/ shell/http/branch/parallel/merge/dataset_*/transform/end) with one or more triggers (cron, channel, webhook, manual, schedule_at, error). The engine walks the graph node-by-node, persists state per run, and reuses existing wick infra (channels, connectors, providers, pool).
See `internal/docs/workflow-design.md` for the full contract.
Index ¶
- Constants
- type ClassifyExample
- type DatasetBinding
- type DatasetOrder
- type Edge
- type EnvField
- type EnvOption
- type Event
- type ExecError
- type Executor
- type Graph
- type Node
- type NodeError
- type NodeOutput
- type NodeSession
- type NodeType
- type OnErrorBinding
- type Override
- type QueuePolicy
- type RenderCtx
- type RetryPolicy
- type RunContext
- type RunEvent
- type RunRef
- type RunState
- type SessionRec
- type SwitchCase
- type Trigger
- type TriggerType
- type Whitelist
- type Workflow
- type WorkflowRef
- type WorkflowState
Constants ¶
const ( OverflowDropOldest = "drop_oldest" OverflowDropNew = "drop_new" OverflowReject = "reject" )
Overflow policy values.
const ( FailHalt = "halt" FailSkip = "skip" FailFallback = "fallback" )
OnFailure values.
const ( MergeObject = "object" MergeArray = "array" MergeFirst = "first" MergeLast = "last" )
MergeStrategy values.
const ( SessionNew = "new" SessionRoot = "root" SessionPersistent = "persistent" )
Session modes for agent/classify nodes.
Legacy values "root" and "persistent" predate the pool-integration design; they remain in the constant list so loaders that touch old YAML round-trip cleanly, but the engine treats them as equivalent to the per-run default (no override). New workflows should use the `session_init` node instead — see internal/docs/workflow/pool.md.
const ( SessionPresetWorkflowRun = "workflow_run" SessionPresetWorkflowGlobal = "workflow_global" SessionPresetNew = "new" )
Session preset values for the `session_init` node. `preset:` and `id:` are mutually exclusive — when `id:` is set the executor renders it as a template; otherwise it falls back to the preset pattern.
const ( StatusQueued = "queued" StatusRunning = "running" StatusPaused = "paused" StatusSuccess = "success" StatusFailed = "failed" )
RunStatus values.
const ( EventNodeStarted = "node_started" EventNodeCompleted = "node_completed" EventNodeFailed = "node_failed" EventNodeSkipped = "node_skipped" EventEdgeTraversed = "edge_traversed" EventWorkflowStarted = "workflow_started" EventWorkflowCompleted = "workflow_completed" EventWorkflowFailed = "workflow_failed" )
Event types emitted to events.jsonl.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ClassifyExample ¶
ClassifyExample is a few-shot prompt example.
type DatasetBinding ¶
type DatasetBinding struct {
Name string `yaml:"name"`
Ref string `yaml:"ref"`
Mode string `yaml:"mode,omitempty"`
ExpectedVersion int `yaml:"expected_version,omitempty"`
}
DatasetBinding wires a dataset alias to a dataset slug.
type DatasetOrder ¶
type DatasetOrder struct {
Column string `yaml:"column"`
Direction string `yaml:"direction,omitempty"`
}
DatasetOrder is one order-by clause.
type Edge ¶
type Edge struct {
From string `yaml:"from"`
To string `yaml:"to"`
Case string `yaml:"case,omitempty"`
Label string `yaml:"label,omitempty"`
}
Edge is a directed connection from one node to another. Case is only meaningful when From is a classify or branch node.
type EnvField ¶
type EnvField struct {
Name string `yaml:"name"`
Widget string `yaml:"widget,omitempty"`
Desc string `yaml:"desc,omitempty"`
Default string `yaml:"default,omitempty"`
Required bool `yaml:"required,omitempty"`
Locked bool `yaml:"locked,omitempty"`
Hidden bool `yaml:"hidden,omitempty"`
Options []EnvOption `yaml:"options,omitempty"`
VisibleWhen map[string]string `yaml:"visible_when,omitempty"`
}
EnvField is one entry of the workflow's env schema.
type Event ¶
type Event struct {
Type string `json:"type"`
Subtype string `json:"subtype,omitempty"`
Channel string `json:"channel,omitempty"`
At time.Time `json:"at"`
Payload map[string]any `json:"payload,omitempty"`
// TriggerID is the workflow.Trigger.ID of the entry that matched
// this dispatch. Populated by the router immediately before
// enqueue; empty for manual / RunNow paths where no specific
// trigger fired. workflow_watch(trigger_id=...) reads this to
// filter runs back to the originating trigger.
TriggerID string `json:"trigger_id,omitempty"`
}
Event is the trigger payload passed to a run. Minimal envelope — channel/transport-specific keys (user, text, thread, chat_id, callback_id, path, …) live in Payload so each integration owns its own shape. Workflow templates reference them as `{{.Event.Payload.<key>}}`.
Type identifies the trigger family ("channel" | "webhook" | "cron" | "manual" | "error" | "schedule_at"). Subtype is the within-family discriminator: for channel events it's the event name ("message", "block_action", …); for webhooks empty; for cron the schedule expression label; for error the source workflow.
Channel is the module name when Type=="channel" ("slack", "telegram", …). Empty for non-channel triggers.
type ExecError ¶
ExecError wraps an executor failure with node identity. Engine promotes this to state.Error.
type Executor ¶
type Executor interface {
Execute(ctx context.Context, node Node, rctx *RunContext) (NodeOutput, error)
}
Executor runs one node body. Engine resolves the executor by node.Type and dispatches with the rendered context.
type Graph ¶
type Graph struct {
Entry string `yaml:"entry"`
Nodes []Node `yaml:"nodes"`
Edges []Edge `yaml:"edges"`
}
Graph is the DAG body: flat node list + separate edge list.
type Node ¶
type Node struct {
// Common
ID string `yaml:"id"`
Type NodeType `yaml:"type"`
Label string `yaml:"label,omitempty"`
Description string `yaml:"description,omitempty"`
TimeoutSec int `yaml:"timeout_sec,omitempty"`
Retry *RetryPolicy `yaml:"retry,omitempty"`
OnFailure string `yaml:"on_failure,omitempty"`
Fallback string `yaml:"fallback,omitempty"`
OutputSchema map[string]any `yaml:"output_schema,omitempty"`
// parallel
Branches []string `yaml:"branches,omitempty"`
// merge
Inputs []string `yaml:"inputs,omitempty"`
Strategy string `yaml:"strategy,omitempty"`
// classify + agent
Provider string `yaml:"provider,omitempty"`
Preset string `yaml:"preset,omitempty"`
Prompt string `yaml:"prompt,omitempty"`
PromptFile string `yaml:"prompt_file,omitempty"`
Session string `yaml:"session,omitempty"` // agent: "new" → fresh UUID; "" → inherit rc.DefaultAgentSessionID. Also used by session_init for legacy aliasing.
// agent override — copy resolved sessionID from another node in
// this run. Must reference an upstream agent or session_init node.
SessionFrom string `yaml:"session_from,omitempty"`
// session_init — preset shortcut OR rendered template id. Mutually
// exclusive; SessionID wins when both set. `Preset` reuses the
// classify/agent Preset field above for YAML brevity.
SessionID string `yaml:"session_id,omitempty"`
// classify
OutputCases []string `yaml:"output_cases,omitempty"`
StructuredOutput *bool `yaml:"structured_output,omitempty"`
Normalize *bool `yaml:"normalize,omitempty"`
FuzzyMatch bool `yaml:"fuzzy_match,omitempty"`
RetryOnMismatch int `yaml:"retry_on_mismatch,omitempty"`
ConfidenceThreshold float64 `yaml:"confidence_threshold,omitempty"`
Examples []ClassifyExample `yaml:"examples,omitempty"`
// agent
Workspace string `yaml:"workspace,omitempty"`
Skills []string `yaml:"skills,omitempty"`
Tools []string `yaml:"tools,omitempty"`
MaxTurns int `yaml:"max_turns,omitempty"`
// channel (action) — Channel field name avoided clash with Event.Channel
ChannelName string `yaml:"channel,omitempty"`
Op string `yaml:"op,omitempty"`
Args map[string]any `yaml:"args,omitempty"`
// ArgModes records each arg's editor mode: "fixed" = literal value
// (executor skips template render), "expression" = Go template (the
// default behaviour kept for backward compat when ArgModes has no
// entry for a key). Persisted so the inspector restores the toggle
// state and so safer-by-default semantics survive a publish round
// trip. Defaults to template render when an arg key is missing
// here, matching pre-ArgModes workflows.
ArgModes map[string]string `yaml:"arg_modes,omitempty"`
// connector — uses row_id for instance (dataset_* nodes own `row:`)
Module string `yaml:"module,omitempty"`
Row string `yaml:"row_id,omitempty"`
// shell
Command []string `yaml:"command,omitempty"`
ShellEnv map[string]string `yaml:"env,omitempty"`
Cwd string `yaml:"cwd,omitempty"`
ParseOutput string `yaml:"parse_output,omitempty"`
// http
Method string `yaml:"method,omitempty"`
URL string `yaml:"url,omitempty"`
Headers map[string]string `yaml:"headers,omitempty"`
Query map[string]string `yaml:"query,omitempty"`
Body string `yaml:"body,omitempty"`
ParseResponse string `yaml:"parse_response,omitempty"`
// db_query — uses `sql:` key (HTTP node already owns `query:` for query params)
Database string `yaml:"database,omitempty"`
SQL string `yaml:"sql,omitempty"`
SQLArgs []string `yaml:"sql_args,omitempty"`
// transform
Engine string `yaml:"engine,omitempty"`
Input string `yaml:"input,omitempty"`
Expression string `yaml:"expression,omitempty"`
// go_script — full Go program. Engine pipes RenderCtx JSON to
// stdin, parses stdout as JSON for the result.
Code string `yaml:"code,omitempty"`
// branch
Expr string `yaml:"expr,omitempty"`
// switch — first-match-wins rule list. Each rule's `when` is a Go
// template that renders to a bool (supports the same binary ops as
// `branch`: ==, !=, <, <=, >, >=) or any non-empty string (truthy).
// First rule whose `when` evaluates true wins; engine emits
// Verdict=<rule.case> so the edge `case: <label>` filter routes
// downstream. DefaultCase fires when no rule matches.
Cases []SwitchCase `yaml:"cases,omitempty"`
DefaultCase string `yaml:"default_case,omitempty"`
// dataset_*
Dataset string `yaml:"dataset,omitempty"`
Where map[string]any `yaml:"where,omitempty"`
Key map[string]any `yaml:"key,omitempty"`
RowValues map[string]any `yaml:"row,omitempty"`
OrderBy []DatasetOrder `yaml:"order_by,omitempty"`
Limit int `yaml:"limit,omitempty"`
Offset int `yaml:"offset,omitempty"`
// end
Result string `yaml:"result,omitempty"`
}
Node is a single step in the graph. Fields are a flat union — only the subset relevant to Type is read by the executor. Validator rejects nodes that set fields outside their type.
type NodeError ¶
type NodeError struct {
Node string `json:"node"`
Type string `json:"type"`
Message string `json:"message"`
Stack string `json:"stack,omitempty"`
}
NodeError captures a failed node's diagnostic.
type NodeOutput ¶
type NodeOutput struct {
Verdict string `json:"verdict,omitempty"`
Confidence float64 `json:"confidence,omitempty"`
Reasoning string `json:"reasoning,omitempty"`
Result any `json:"result,omitempty"`
Fields map[string]any `json:"-"` // merged into top-level when serialized
}
NodeOutput is what an executor returns. It is stored in state.Outputs[node.ID] and surfaced via `{{.Node.<id>.X}}`. The `Verdict` field is the routing key for classify/branch nodes.
type NodeSession ¶
NodeSession is the per-agent-node session override. Empty struct means "use rc.DefaultAgentSessionID (or the engine fallback)".
- From — copy the resolved sessionID from another node in this run (must be an upstream agent/session_init node, validator rejects forward refs + cycles)
- Mode — "new" forces a fresh UUID per call; empty inherits
type NodeType ¶
type NodeType string
NodeType is the discriminator for the polymorphic Node body.
const ( NodeClassify NodeType = "classify" NodeAgent NodeType = "agent" NodeChannel NodeType = "channel" NodeConnector NodeType = "connector" NodeShell NodeType = "shell" NodeSwitch NodeType = "switch" NodeGoScript NodeType = "go_script" NodePython NodeType = "python" NodeHTTP NodeType = "http" NodeDBQuery NodeType = "db_query" NodeTransform NodeType = "transform" NodeBranch NodeType = "branch" NodeParallel NodeType = "parallel" NodeMerge NodeType = "merge" NodeEnd NodeType = "end" NodeDatasetGet NodeType = "dataset_get" NodeDatasetExists NodeType = "dataset_exists" NodeDatasetQuery NodeType = "dataset_query" NodeDatasetInsert NodeType = "dataset_insert" NodeDatasetUpsert NodeType = "dataset_upsert" NodeDatasetDelete NodeType = "dataset_delete" NodeDatasetCount NodeType = "dataset_count" NodeSessionInit NodeType = "session_init" )
func (NodeType) IsBranchSource ¶
IsBranchSource reports whether nodes of this type produce a verdict that filters outgoing edges by `case:`.
func (NodeType) IsDatasetNode ¶
IsDatasetNode reports whether t is one of the dataset_* variants.
type OnErrorBinding ¶
type OnErrorBinding struct {
TriggerWorkflow string `yaml:"trigger_workflow"`
Severity string `yaml:"severity,omitempty"`
IncludeState bool `yaml:"include_state,omitempty"`
IncludeNodeOutput bool `yaml:"include_node_output,omitempty"`
}
OnErrorBinding declares which error-handler workflow to fire on failure.
type QueuePolicy ¶
type QueuePolicy struct {
MaxSize int `yaml:"max_size,omitempty"`
OnOverflow string `yaml:"on_overflow,omitempty"` // drop_oldest | drop_new | reject
}
QueuePolicy controls per-workflow concurrency.
type RenderCtx ¶
type RenderCtx struct {
Event Event
Node map[string]any
Env map[string]string
Secret map[string]string
Workflow WorkflowRef
Run RunRef
Dataset map[string]any
}
RenderCtx is the root object exposed to Go templates inside node bodies. Fields map 1:1 to the design refs:
{{.Event.X}} — trigger event payload
{{.Node.<id>.X}} — output of completed node X
{{.Env.X}} — non-secret workflow env value
{{.Secret.X}} — encrypted secret, decrypted on lookup
{{.Workflow.X}} — workflow metadata
{{.Run.X}} — runtime metadata
{{.Dataset.<alias>}} — dataset binding from datasets: list
type RetryPolicy ¶
RetryPolicy on a node.
type RunContext ¶
type RunContext struct {
Workflow Workflow
Event Event
Outputs map[string]any
EnvValues map[string]string
Secrets map[string]string
RunID string
NodeOutputs map[string]NodeOutput
// TriggerNodeID is the canvas/yaml node id under which the run's
// firing trigger surfaces in {{.Node.<id>.…}}. Set by the engine
// when pickEntry resolves a trigger row (uses Trigger.ID when set,
// else falls back to the entry node id). Empty for legacy runs
// (graph.entry fallback) — those still expose data via .Event.
TriggerNodeID string
// DefaultAgentSessionID is set by an upstream `session_init` node
// and consumed by downstream `agent` nodes that don't override
// session: themselves. Empty = engine falls back to the per-run
// pattern "wf:<id>:run:<runID>". See pool.md for the resolver
// order.
DefaultAgentSessionID string
// AgentSessionIDs maps node ID → resolved sessionID for every
// agent / session_init node that has run. Lets downstream agent
// nodes opt into "reuse this upstream's subprocess" via
// `session_from: <node-id>` without re-resolving the template.
AgentSessionIDs map[string]string
}
RunContext carries per-run state into executors. It's a thin pointer-receiver wrapper so executors can read outputs from upstream nodes + read env/secrets without taking the entire engine.
func (*RunContext) RenderCtx ¶
func (r *RunContext) RenderCtx() RenderCtx
RenderCtx materializes a RenderCtx from the RunContext for template rendering inside an executor.
type RunEvent ¶
type RunEvent struct {
TS time.Time `json:"ts"`
Event string `json:"event"`
Node string `json:"node,omitempty"`
Case string `json:"case,omitempty"`
Data map[string]any `json:"data,omitempty"`
}
RunEvent is one line in events.jsonl.
type RunState ¶
type RunState struct {
RunID string `json:"run_id"`
WorkflowID string `json:"workflow_id"`
Version int `json:"version"`
Status string `json:"status"`
Entry string `json:"entry"`
Current []string `json:"current"`
Completed []string `json:"completed"`
Failed []string `json:"failed,omitempty"`
Skipped []string `json:"skipped,omitempty"`
Outputs map[string]any `json:"outputs"`
Event Event `json:"event"`
Error *NodeError `json:"error,omitempty"`
Sessions map[string]SessionRec `json:"sessions,omitempty"`
StartedAt time.Time `json:"started_at"`
UpdatedAt time.Time `json:"updated_at"`
EndedAt *time.Time `json:"ended_at,omitempty"`
}
RunState is the persisted execution snapshot.
type SessionRec ¶
type SessionRec struct {
PID int `json:"pid"`
StartedAt time.Time `json:"started_at"`
LastHeartbeat time.Time `json:"last_heartbeat"`
TranscriptPath string `json:"transcript_path,omitempty"`
}
SessionRec tracks a long-lived agent subprocess across nodes.
type SwitchCase ¶
SwitchCase is one rule row for `switch` nodes. `When` is a Go template expression (e.g. `{{.Event.Payload.action}} == "approve"`) rendered against the run context; `Case` is the verdict label the engine emits when the rule wins, matched against outgoing edge `case:` filters.
type Trigger ¶
type Trigger struct {
ID string `yaml:"id,omitempty"`
Type TriggerType `yaml:"type"`
EntryNode string `yaml:"entry_node,omitempty"`
// cron
Schedule string `yaml:"schedule,omitempty"`
Timezone string `yaml:"timezone,omitempty"`
// channel
ChannelName string `yaml:"channel,omitempty"`
Event string `yaml:"event,omitempty"`
Target string `yaml:"target,omitempty"`
Match map[string]any `yaml:"match,omitempty"`
// MatchEnabled gates whether the router applies Match at dispatch
// time. false (default) = dump-all (every event of this type fires
// the workflow); true = router skips runs where Match values don't
// pair with the event payload. Backward compat for workflows that
// stored match: without an explicit toggle is to treat a populated
// Match map as enabled — see router.go for the resolution rule.
MatchEnabled bool `yaml:"match_enabled,omitempty"`
MatchModes map[string]string `yaml:"match_modes,omitempty"`
Whitelist *Whitelist `yaml:"whitelist,omitempty"`
DedupTTLSec int `yaml:"dedup_ttl_sec,omitempty"`
ReplySource *bool `yaml:"reply_source,omitempty"`
// webhook
Path string `yaml:"path,omitempty"`
Method string `yaml:"method,omitempty"`
SecretRef string `yaml:"secret_ref,omitempty"`
ParseBody string `yaml:"parse_body,omitempty"`
BodyToVar string `yaml:"body_to_var,omitempty"`
// manual
Label string `yaml:"label,omitempty"`
RequireRole string `yaml:"require_role,omitempty"`
// schedule_at
At time.Time `yaml:"at,omitempty"`
DeleteAfter bool `yaml:"delete_after,omitempty"`
// error
SourceWorkflow string `yaml:"source_workflow,omitempty"`
Severity []string `yaml:"severity,omitempty"`
NodeTypes []string `yaml:"node_types,omitempty"`
}
Trigger is one polymorphic trigger entry. Fields are a flat union like Node — the validator gates each field to its Type.
ID is the stable canvas identifier (e.g. "trigger_manual", "trigger-cron-2"). The codec uses it to merge per-trigger metadata (channel name, schedule, …) across save cycles so the canvas can re-wire EntryNode without losing the config the user typed in the inspector. Optional in YAML — workflows hand-edited without canvas can omit it.
func (Trigger) MarshalYAML ¶
MarshalYAML normalizes Match before serialization — picker values stored as JSON strings (`[{"id":"C1","name":"#ch"}]`) are expanded to native YAML slices so the workflow.yaml is human-readable and AI-writable without JSON escaping.
type TriggerType ¶
type TriggerType string
TriggerType discriminator for the polymorphic Trigger body.
const ( TriggerCron TriggerType = "cron" TriggerChannel TriggerType = "channel" TriggerWebhook TriggerType = "webhook" TriggerManual TriggerType = "manual" TriggerScheduleAt TriggerType = "schedule_at" TriggerError TriggerType = "error" )
type Whitelist ¶
type Whitelist struct {
Users []string `yaml:"users,omitempty"`
Groups []string `yaml:"groups,omitempty"`
IPs []string `yaml:"ips,omitempty"`
}
Whitelist filters who can fire a trigger.
type Workflow ¶
type Workflow struct {
ID string `yaml:"id"`
Version int `yaml:"version"`
Name string `yaml:"name"`
Description string `yaml:"description,omitempty"`
Enabled bool `yaml:"enabled"`
MaxDurationSec int `yaml:"max_duration_sec,omitempty"`
Triggers []Trigger `yaml:"triggers"`
Queue QueuePolicy `yaml:"queue,omitempty"`
Env []EnvField `yaml:"env,omitempty"`
Datasets []DatasetBinding `yaml:"datasets,omitempty"`
Graph Graph `yaml:"graph"`
OnError *OnErrorBinding `yaml:"on_error,omitempty"`
CreatedBy string `yaml:"created_by,omitempty"`
CreatedAt time.Time `yaml:"created_at,omitempty"`
Canvas map[string]any `yaml:"_canvas,omitempty"`
}
Workflow is the root document parsed from `workflow.yaml`.
ID is the stable folder name (UUID for canvas-created workflows, arbitrary id for legacy hand-edited ones). Display title lives in Name and is freely renameable — the folder/URL/log paths stay anchored to ID so run history survives a rename.
type WorkflowRef ¶
WorkflowRef is the small subset of Workflow accessible to templates.
type WorkflowState ¶
type WorkflowState struct {
Approved bool `json:"approved"`
ApprovedBy string `json:"approved_by,omitempty"`
ApprovedAt *time.Time `json:"approved_at,omitempty"`
ApprovedVersion int `json:"approved_version,omitempty"`
ContentHash string `json:"content_hash,omitempty"`
GovernanceMode string `json:"governance_mode,omitempty"`
OverrideReason string `json:"override_reason,omitempty"`
}
WorkflowState is the persisted approval/governance snapshot.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package canvas mutates a Workflow's graph atomically.
|
Package canvas mutates a Workflow's graph atomically. |
|
Package channel is a thin adapter over internal/agents/channels.
|
Package channel is a thin adapter over internal/agents/channels. |
|
Package connector is the workflow-facing index of connector modules.
|
Package connector is the workflow-facing index of connector modules. |
|
Package cost aggregates per-node + per-workflow LLM usage.
|
Package cost aggregates per-node + per-workflow LLM usage. |
|
Package dataset is the workflow-facing data store.
|
Package dataset is the workflow-facing data store. |
|
Package engine — multi-subscriber event broker.
|
Package engine — multi-subscriber event broker. |
|
Package env validates and resolves the workflow's env schema + values.
|
Package env validates and resolves the workflow's env schema + values. |
|
Package guard reviews a workflow body for safety violations before publish.
|
Package guard reviews a workflow body for safety violations before publish. |
|
Package integration is the workflow-side surface for per-event + per-action node descriptors.
|
Package integration is the workflow-side surface for per-event + per-action node descriptors. |
|
Package mcp — workflow_describe implementation.
|
Package mcp — workflow_describe implementation. |
|
Package nodes contains the concrete Executor impls for every Node type.
|
Package nodes contains the concrete Executor impls for every Node type. |
|
Package parse decodes and validates workflow.yaml bodies.
|
Package parse decodes and validates workflow.yaml bodies. |
|
Package provider is the abstraction the workflow engine talks to for classify + agent nodes.
|
Package provider is the abstraction the workflow engine talks to for classify + agent nodes. |
|
Package scaffold provides workflow starter templates used by `workflow_create` MCP op + UI "New workflow" form.
|
Package scaffold provides workflow starter templates used by `workflow_create` MCP op + UI "New workflow" form. |
|
Package service is the CRUD facade over `<BaseDir>/workflows/`.
|
Package service is the CRUD facade over `<BaseDir>/workflows/`. |
|
Package setup wires every workflow subpkg together.
|
Package setup wires every workflow subpkg together. |
|
Package state persists per-run state.json + events.jsonl under `<BaseDir>/workflows/<id>/runs/<run-id>/`.
|
Package state persists per-run state.json + events.jsonl under `<BaseDir>/workflows/<id>/runs/<run-id>/`. |
|
Package template renders Go text/template strings against a workflow.RenderCtx.
|
Package template renders Go text/template strings against a workflow.RenderCtx. |
|
Package trigger is the dispatch + queue + dedup layer between trigger sources (cron, channel adapter, webhook, manual, error) and the engine.
|
Package trigger is the dispatch + queue + dedup layer between trigger sources (cron, channel adapter, webhook, manual, error) and the engine. |
|
Package wftest runs workflow test cases loaded from `__tests__/` fixtures.
|
Package wftest runs workflow test cases loaded from `__tests__/` fixtures. |