v0.4.0-alpha

v0.4.0-alpha

Released: February 2026

Flow lifecycle hooks, gate nodes for approval workflows, child workflow composition, dynamic flow templates, and two new providers.

New Features

Flow Hooks

Flows now support lifecycle callbacks at flow, step, and node boundaries. All callbacks are optional — nil callbacks are safely skipped.

flow := core.NewFlow("monitored-pipeline").
    TriggeredBy(core.Schedule("*/15 * * * *")).
    WithHooks(&core.FlowHooks{
        BeforeFlow: func(ctx core.HookContext) {
            log.Info("flow starting", "flow", ctx.FlowName)
        },
        AfterNode: func(ctx core.HookContext) {
            log.Info("node completed",
                "node", ctx.NodeName,
                "duration", ctx.Duration,
                "error", ctx.Error,
            )
        },
        OnCost: func(entry core.CostEntry) {
            metrics.RecordLLMCost(entry.Model, entry.CostUSD)
        },
    }).
    Then(fetchNode).
    Then(processNode).
    Build()

New APIs:

APIDescription
FlowHooksStruct holding optional lifecycle callbacks
HookContextCarries FlowName, StepName, NodeName, Duration, Error
CostEntryLLM cost event with Model, Provider, TokensIn/Out, CostUSD
.WithHooks(*FlowHooks)Attach hooks to a flow via FlowBuilder

Hooks run inside Temporal’s deterministic workflow context. They must not perform I/O directly — enqueue activities for side effects.

Gate Nodes

Gates pause flow execution until an external Temporal signal is received. Use them for approval workflows, manual review steps, or wait-for-event patterns.

flow := core.NewFlow("deploy-with-approval").
    TriggeredBy(core.Manual("api")).
    Then(runTestsNode).
    ThenGate("approval", core.GateConfig{
        SignalName: "deploy-approval",
        Timeout:    24 * time.Hour,
    }).
    Then(deployNode).
    Build()

To approve from outside the workflow:

client.SignalWorkflow(ctx, workflowID, "", "deploy-approval", core.GateResult{
    Approved:  true,
    DecidedBy: "alice@company.com",
    Reason:    "Tests passed, deploy approved",
})

New APIs:

APIDescription
GateNodeExecutableNode that waits for a signal
GateConfig{SignalName, Timeout}Signal name to wait for and optional timeout
GateResultDecision payload with Approved, DecidedBy, Reason, Metadata
GateTimeoutErrorReturned when timeout expires before signal
.ThenGate(name, config)Add a gate step via FlowBuilder

Child Flow Nodes

Spawn child workflows from a parent flow for fan-out processing, batch operations, or nested workflow composition.

processingFlow := core.NewFlow("process-item").
    TriggeredBy(core.Manual("internal")).
    Then(processNode).
    Build()

parentFlow := core.NewFlow("batch-processor").
    TriggeredBy(core.Schedule("0 * * * *")).
    Then(fetchItemsNode).
    ThenChildren("process-items", core.ChildFlowConfig{
        Flow: processingFlow,
        InputMapper: func(state *core.FlowState) []core.FlowInput {
            items := core.Get[FetchOutput](state, "fetch-items")
            inputs := make([]core.FlowInput, len(items.Items))
            for i, item := range items.Items {
                inputs[i] = core.FlowInput{
                    Data: map[string][]byte{
                        "item": marshal(item),
                    },
                }
            }
            return inputs
        },
    }).
    Then(aggregateNode).
    Build()

New APIs:

APIDescription
ChildFlowNodeExecutableNode that spawns child workflows
ChildFlowConfig{Flow, InputMapper, Sequential}Child flow, input derivation function, execution mode
ChildFlowResults{States, Errors, Count}Aggregated results from all child workflows
.ThenChildren(name, config)Add a child flow step via FlowBuilder

Parallel execution by default. Set Sequential: true to process children one at a time, stopping on first error.

Flow Templates

Construct flows dynamically at runtime when step composition depends on configuration, feature flags, or user input.

tmpl := core.NewFlowTemplate("dynamic-pipeline").
    TriggeredBy(core.Manual("api"))

tmpl.AddStep(fetchNode)

if config.NeedsApproval {
    tmpl.AddGate("review", core.GateConfig{
        SignalName: "review-approval",
        Timeout:    48 * time.Hour,
    })
}

if config.ParallelProcessing {
    tmpl.AddParallel("process-all", transformNode, enrichNode)
} else {
    tmpl.AddStep(transformNode)
    tmpl.AddStep(enrichNode)
}

tmpl.AddStep(storeNode)

flow := tmpl.Build()

New APIs:

APIDescription
FlowTemplateRuntime flow builder (vs compile-time FlowBuilder)
NewFlowTemplate(name)Create a new template
.AddStep(node)Add a sequential step
.AddParallel(name, ...nodes)Add a parallel step
.AddGate(name, config)Add a gate step
.AddChildren(name, config)Add a child flow step
.AddConditional(pred, then, else)Add a conditional branch
.Build()Validate and return a *Flow

InputData Magic Marker

Access webhook payload data from activity input structs using the InputData marker.

flow := core.NewFlow("webhook-handler").
    TriggeredBy(core.Webhook("/bitbucket")).
    Then(bitbucket.ParseWebhook(bitbucket.ParseWebhookInput{
        RawPayload: core.InputData("webhook_payload"),
    })).
    Then(processNode).
    Build()
APIDescription
core.InputData(key)Creates {{input:key}} marker resolved at execution time
FlowState.GetInputData(key)Retrieves raw input data by key

New Providers

Bitbucket (resolute-bitbucket)

Bitbucket integration for webhook-driven workflows and PR automation.

go get github.com/resolute-sh/resolute-bitbucket@v0.4.0-alpha

Activities:

ActivityDescription
ParseWebhookParses raw webhook payload into structured PRMetadata
AddCommentPosts a comment to a Bitbucket pull request

Utility: ExtractTicketID(branch) extracts Jira ticket IDs from branch names.

Slack (resolute-slack)

Slack integration for sending notifications via incoming webhooks.

go get github.com/resolute-sh/resolute-slack@v0.4.0-alpha

Activities:

ActivityDescription
SendMessageSends messages via Slack webhook with Block Kit support

Installation

go get github.com/resolute-sh/resolute@v0.4.0-alpha

Migration from v0.3.0-alpha

No breaking changes to existing APIs. All new features are additive:

  • WithHooks, ThenGate, ThenChildren are optional FlowBuilder methods
  • FlowTemplate is an alternative to FlowBuilder — existing flows are unaffected
  • InputData is a new marker alongside existing CursorFor and OutputRef
  • New providers are independent modules installed separately

Full Changelog

v0.3.0-alpha…v0.4.0-alpha