Skip to documentation
Documentation navigation

Documentation navigation

Documentation / guides

Workflow Runtime and Supervisors

Operate a session-owned Supervisor, durable start acknowledgement, adoption, and shutdown.

developer

Supervisor is the session boundary around registered definitions. It owns one session lease, coordinates run controllers, and joins Workflows registry state to the Flow checkpoint truth. A process may construct definitions and registries before a live session exists, but execution begins only after Activate has bound the supervisor to Harness session services.

Construct, activate, and shut down

NewSupervisor requires a nonzero SessionID, a Catalog, a RunRegistry, an InputStore, and a storage.Leaser. It defaults to a UTC clock, a five-second shutdown timeout, and four workers. MaxWorkers must be between 1 and 64.

Activate(ctx, services) does the following in order:

  1. Validate the late-bound tool.SessionResourceServices value.
  2. Acquire the session ownership lease.
  3. Store the owner context and workflow activity publisher.
  4. Start watching for lease loss.
  5. List existing runs and schedule reconciliation for nonterminal records.

Activation is single-owner. A second active supervisor receives ErrSupervisorActive; a closed supervisor receives ErrSupervisorClosed. If loading existing runs fails, activation releases the lease and returns to the inactive state.

Shutdown stops accepting work, cancels the owner context, waits for worker completion within the configured timeout, releases the lease, and waits for the lease watcher. A timeout is reported as ErrShutdownTimeout. Callers should still treat the supervisor as closed after shutdown.

// The Harness process creates the publishers and leaser. Workflows only needs
// their interfaces, so a storage backend can change without changing the run
// controller.
supervisor, err := workflows.NewSupervisor(workflows.SupervisorConfig{
	SessionID:       sessionID,
	Catalog:         catalog,
	Registry:        registry,
	Inputs:          inputs,
	Leaser:          leaser,
	ShutdownTimeout: 5 * time.Second,
	MaxWorkers:      4,
})
if err != nil {
	return err
}

services, err := tool.NewSessionResourceServices(
	processLifecyclePublisher,
	processCompletionNotifier,
	workflowActivityPublisher,
)
if err != nil {
	return err
}
if err := supervisor.Activate(ctx, services); err != nil {
	return err
}
defer supervisor.Shutdown(context.Background())

The service set is described in Harness integration. The surrounding session lifecycle and tool registration are documented in the Harness module reference.

Start is durable before it is asynchronous

Supervisor.Start(ctx, runID) returns three values: a channel that closes after the Flow run has written its durable seed, a channel for a definite scheduling or start error, and an immediate error for a rejected request. The worker continues toward interruption or completion after the seed acknowledgement. This lets a caller report a real run ID and initial status without waiting for the graph to finish.

The normal sequence is:

%%{init: {"theme": "dark"}}%%
sequenceDiagram
    participant Tool as workflow_run_start
    participant S as Supervisor
    participant R as RunRegistry
    participant F as Flow Runner
    participant C as CheckpointStore
    Tool->>R: Create pending Run
    Tool->>S: Start(run ID)
    S->>R: CAS pending -> running
    S->>F: Start with GraphRunID
    F->>C: Append revision 0 seed
    C-->>S: durable seed
    S-->>Tool: close seeded channel
    F->>C: append later checkpoints
    S->>R: reconcile status and activity cursor

The start worker checks that the registry record is still pending, resolves the exact definition name and version, validates and loads the private input reference, transitions the record to running, and calls Definition.Start with flow.WithGraphRunID. If ownership is lost while the graph is executing, the worker stops making session-owned writes and lets the next owner adopt the checkpoint.

Restart adoption

Activation lists runs by session. Completed and cancelled records are not restarted. Pending, running, interrupted, and failed records are attached to a controller and reconciled. The controller reads the Flow history before deciding what to do:

Registry statusCheckpoint observationAction
pendingNo checkpointStart the graph
pendingExisting checkpointPromote through running, then adopt
runningRunning checkpointCall the typed definition’s Adopt capability when available
running or interruptedCompleted or cancelled checkpointRepair the registry from checkpoint truth
Any active statusDefinite missing, corrupt, or mismatched checkpointMark the registry run failed
failedHistory available or intentionally absentReconcile the bounded failure activity only

Adopt is not a user resume. It carries no resume payload and is used only to continue a durable running Flow checkpoint after a supervisor restart. An interrupted checkpoint still needs an explicit Resume call with a definition-validated payload. That distinction prevents an owner restart from inventing user intent.

Operational controls

The supervisor exposes focused controls:

MethodBehavior
Start(ctx, runID)Schedule a pending run and wait only for the durable seed acknowledgement
Resume(ctx, runID, payload)Require an interrupted run, validate the payload through its definition, and continue Flow
Cancel(ctx, runID, reason)Persist cancellation intent, interrupt in-flight execution, and reconcile the terminal checkpoint
History(ctx, runID, afterRevision, afterEventID, limit)Project bounded activity metadata from durable checkpoint history
WaitIdle(ctx)Wait for scheduled work, then return the first recorded worker error
LastError()Read the first worker error retained by the supervisor
Shutdown(ctx)Stop work, release ownership, and report timeout or release failure

WaitIdle does not mean that a run is completed. It means the supervisor has no currently scheduled workers. A long-lived supervisor may schedule more work after the call.

Source

Proof

← back to documentation