Running Workflows
Runsight has two execution modes: production runs on the main branch and simulation runs on disposable branches. Every run is persisted as a database record with per-block node tracking, parent-child linkage for sub-workflows, and a commit SHA tying the run back to the workflow YAML that executed.
Run lifecycle
Section titled “Run lifecycle”A run moves through a fixed state machine:
| State | Meaning |
|---|---|
pending | Created in the database, waiting for a concurrency slot |
running | Actively executing blocks |
completed | All blocks finished successfully |
failed | A block raised an unhandled error or a budget limit was exceeded |
cancelled | Stopped by user action or server shutdown |
Terminal states (completed, failed, cancelled) are final --- no further transitions are allowed. The valid transitions are:
pending→running,cancelled,failedrunning→completed,failed,cancelled
Production runs
Section titled “Production runs”A production run executes the workflow YAML as committed on the main branch.
Production runs can be started from the GUI when there are no unsaved workflow changes, or through the external Direct API. Direct API invocation uses POST /api/workflows/{workflow_id}/runs, always records source: "api", always resolves branch: "main", and only runs workflows committed on main and explicitly enabled with enabled: true. Callers cannot supply source, branch, provenance, delivery, simulation, or idempotency fields in the request body.
When a production run starts:
- The API resolves the committed
mainworkflow YAML for the requested workflow and verifies that the committed snapshot hasenabled: true. - A
Runrecord is created withstatus: pendingandbranch: "main". - The execution service acquires a concurrency slot (default: 5 concurrent runs), then transitions the run to
running. - The engine parses the YAML, builds the workflow graph, and wraps every LLM block in an
IsolatedBlockWrapperbacked byUnixLocalHarness. - Each block executes sequentially through the transition graph. A
RunNoderecord is created per block. - For LLM blocks (linear, gate, synthesize, dispatch), the wrapper creates a
WorkspaceRunRequest.UnixLocalHarnesscreates a workspace session, materializes declared files, and starts a local Unix worker inside the session workspace. - The worker discovers host IPC through
RUNSIGHT_IPC_CONFIG_B64=<base64-json IPCClientConfig>. Model calls, HTTP access, file access, and tool execution are mediated by host-side handlers built from per-run host bindings and registries. - The harness validates the worker
ResultEnvelope, cleans up the workspace according to policy, and returns the result to the normal block wrapper path. - On completion, the observer writes
status: completedwith final cost and token totals.
Direct API runs ignore dirty working tree edits to workflow YAML and nested workflow YAML. Referenced workflow blocks are resolved through the same committed snapshot. Parser and discovery paths receive the resolved git ref for snapshot-capable workflow assets, while provider settings, server settings, and API keys remain live runtime configuration from the running server environment. Omitted enabled is treated as disabled for Direct API invocation.
For the exact external HTTP contract, request body, success response, and error codes, see Direct API Invocation.
Simulation runs
Section titled “Simulation runs”When the workflow has unsaved changes (the canvas shows an uncommitted badge), clicking Run creates a simulation run:
- The GUI calls
POST /api/git/sim-branchwith the current YAML draft. - A simulation branch is created with the naming convention
sim/{workflow-slug}/{YYYYMMDD}/{short-id}(e.g.,sim/research-pipeline/20260407/a3f1b). - The run is created with
branch: "sim/research-pipeline/20260407/a3f1b"andsource: "simulation". - Execution proceeds identically to a production run, but reads the YAML from the sim branch snapshot.
Simulation branches are disposable --- they capture the exact state of the workflow at the time of the run, including any unsaved edits. See Git Integration for details on how sim branches work.
The Run record
Section titled “The Run record”Each run is stored as a Run row in the SQLite database. Important fields include:
| Field | Type | Description |
|---|---|---|
id | str | Primary key (UUID) |
workflow_id | str | Which workflow was executed |
workflow_name | str | Human-readable workflow name |
status | RunStatus | Current state (pending / running / completed / failed / cancelled) |
branch | str | Git branch this run executed against; production launch paths set "main" |
source | str | How the run was triggered, such as manual, simulation, or api |
commit_sha | str? | Git commit SHA of the YAML that executed |
total_cost_usd | float | Accumulated LLM cost |
total_tokens | int | Accumulated token count |
error | str? | Error message if the run failed |
fail_reason | str? | Structured failure category (e.g., "budget_exceeded") |
parent_run_id | str? | Parent run ID for sub-workflow runs |
root_run_id | str? | Top-level ancestor run ID |
depth | int | Nesting depth (0 for top-level runs) |
RunNode - per-block tracking
Section titled “RunNode - per-block tracking”Each block execution within a run creates a RunNode record:
| Field | Type | Description |
|---|---|---|
id | str | Composite key: {run_id}:{node_id} |
node_id | str | Block ID from the workflow YAML |
block_type | str | Block type (linear, gate, synthesize, dispatch, code, loop, workflow) |
status | NodeStatus | pending / running / completed / failed |
cost_usd | float | Cost for this block’s LLM calls |
tokens | dict | Token breakdown: {"prompt": N, "completion": N, "total": N} |
output | str? | Block output text |
eval_score | float? | Assertion evaluation score |
eval_passed | bool? | Whether all assertions passed |
child_run_id | str? | For workflow blocks: the child run’s ID |
exit_handle | str? | Which exit port this block took |
Sub-workflow runs
Section titled “Sub-workflow runs”When a workflow contains a workflow block, the child workflow executes as a nested run. The parent RunNode records the child_run_id, and the child Run stores parent_run_id, root_run_id, and depth. This creates a tree of runs that you can query via the API:
curl http://localhost:8000/api/runs/{parent_run_id}/childrenThe child run response includes parent_run_id, root_run_id, and depth fields so you can reconstruct the full execution tree.
Ghost run recovery
Section titled “Ghost run recovery”If the server restarts while runs are in pending or running status, those runs become “ghosts” --- they will never complete because the asyncio task is gone. On startup, the execution service calls fail_ghost_runs() to mark pending and running runs as failed with error: "API server restarted during execution".
Triggering runs
Section titled “Triggering runs”| Method | How |
|---|---|
| GUI | Click the Run button on the canvas topbar. Committed clean workflows run on main; dirty workflows create a simulation branch. |
| Direct API | POST /api/workflows/{workflow_id}/runs with a required body containing only inputs. Direct API runs require a committed main workflow with enabled: true, and use source: "api" and branch: "main". |
See Direct API Invocation for the copyable curl example and full request reference.
Workspace isolation
Section titled “Workspace isolation”Every LLM block runs through the workspace isolation path. API keys, HTTP credentials, URL allowlists, and executable tool references stay in host-only WorkspaceHostBindings and host execution registries. The worker receives only serializable manifests, policy, worker-visible tool metadata, and its config-based IPC connection details.
Request-backed custom HTTP tools seed allowed hosts from their host-side request URL. Dynamic built-in http calls require RUNSIGHT_HTTP_URL_ALLOWLIST on the host; otherwise mediated HTTP is denied before network access.
UnixLocalHarness is the current local workspace harness. It gives each run a fresh workspace root, starts the worker with cwd inside that workspace, scopes mediated file I/O to the same root, validates the result envelope, and cleans up afterward. Unix-local isolation protects host-mediated credentials and workspace access, but it is not a container-grade OS sandbox.
See Workspace Isolation for the full architecture.