Skip to content

Run lifecycle ​

A workflow run starts with a UI command addressed to a run ID. Desktop passes it to orchestration, which hosts InteractiveEngine, connects providers and tools, persists checkpoints, and returns state changes to the UI.

Terms: glossary. Ownership: architecture contract. Each table names the source files for its part of the run.

1. Runtime boundaries ​

flowchart TB
    subgraph UI["ui — presentation"]
        Editor["Editor / Chat / Canvas"]
    end
    subgraph Desktop["desktop — Tauri IPC"]
        Cmd["invoke handlers"]
        Bridge["run-state event bridge"]
    end
    subgraph Orch["orchestration — run host + app state"]
        Backend["AppBackend"]
        Coord["RunCoordinator"]
        Drive["drive_interactive_workflow"]
        ToolPort["ToolPortImpl"]
    end
    subgraph Engine["engine — execution semantics"]
        IE["InteractiveEngine"]
        Ports["AiPort / ToolPort traits"]
    end
    subgraph Prov["providers — LLM transport"]
        AiClient["create_provider → AiClient"]
    end

    Editor -->|invoke| Cmd
    Cmd --> Backend --> Coord --> Drive
    Drive --> IE
    IE --> Ports
    Ports --> AiClient
    IE --> ToolPort
    Drive -->|RunTelemetry| Bridge -->|run-state| Editor
ConcernRule ownerAdapter / host
Workflow graph validity, execution layers, turn semanticsengine (graph/, execution/)orchestration calls validate_workflow before run
Tool approval policy (ApprovalMode)engine (tools/config.rs)orchestration filters tool catalog in tool/registry.rs
When to pause (NeedsInteraction)engine (InteractiveEngine::run)orchestration maps to RunTelemetry + session actions
Tool/subagent I/O, cwd, artifactsorchestration (run/execution/tool_port.rs, adapters/tool_impl/)implements ToolPort
LLM wire format, streaming, AgentErrorproviders (client.rs, rig_adapter/)implements AiPort
Run session, checkpoints, trace projectionorchestration (run/coordinator/, run/state.rs)desktop bridges to UI
Presentation, derived UI stateuinever imports engine/providers
IPC transportdesktopmaps invoke ↔ AppBackend

Invariant: only orchestration/src/run/execution/ may construct InteractiveEngine (arch check enforced).

2. Run lifecycle ​

sequenceDiagram
    participant UI as ui
    participant DT as desktop
    participant BE as AppBackend
    participant RC as RunCoordinator
    participant DR as drive_interactive_workflow
    participant EN as InteractiveEngine
    participant PR as providers (AiPort)
    participant TP as ToolPortImpl

    Note over UI: Author / load (pre-run)
    UI->>DT: save_workflow / load workflows
    DT->>BE: WorkflowCatalog CRUD
    BE->>BE: merge app + project stores

    Note over UI: Start run
    UI->>DT: start_run(workflow, settings, projectId, key, entrypoint?)
    DT->>BE: start_run
    BE->>BE: resolve workspace + probe write access
    BE->>RC: start_run(execution cwd, run root)
    RC->>RC: validate_workflow
    RC->>RC: resolve_execution_cwd
    RC->>RC: prepare_workflow_run (provider router, snapshots)
    RC->>RC: create RunRecord, spawn drive task
    DR->>EN: InteractiveEngine::new / from_checkpoint
    DR->>EN: run(ai_adapter, tool_port, cancel_token)

    loop Until terminal or pause
        EN->>PR: AiPort::invoke_stream(AgentRequest)
        PR-->>EN: AgentTurnOutcome | AgentError
        EN->>TP: ToolPort::execute_batch (auto-approved tools)
        TP-->>EN: ToolBatchOutput
    end

    alt NeedsInteraction
        EN-->>DR: EngineRunResult::NeedsInteraction
        DR->>BE: RunTelemetry (await input/approval/retry)
        BE->>DT: apply_execution_event → emit run-state
        DT->>UI: run-state (WorkflowRunState)
        UI->>DT: submit_user_input / submit_tool_approval / retry_node
        DT->>EN: on_human_input / on_tool_decision / retry_node
        DR->>EN: run(...) again
    else Completed / Failed / Cancelled
        EN-->>DR: terminal EngineRunResult
        DR->>BE: Finished | Error | Aborted
        BE->>DT: final run-state
    end
PhaseEntryKey symbolsOwner
Author / loadEditor save, bootstrapWorkflowCatalog::load_all, save_allorchestration
ValidateRun start, engine constructvalidate_workflow → execution_layersengine
Prepstart_runprepare_workflow_run, resolve_callable_agent_snapshotsorchestration
Drivespawned tokio taskdrive_interactive_workflow, wire_runorchestration
Pause / resumeNeedsInteractionemit_interaction_pause, await_interaction_actionsorchestration wraps engine
Complete / failEngineRunResult terminalFinished(RunReport), NodeFailed, Cancelledengine decides; orchestration projects

3. State machines ​

3.1 InteractiveEngine / EngineRunResult ​

Source: crates/engine/src/execution/interactive_engine/mod.rs → EngineRunResult, InteractiveEngine::run

stateDiagram-v2
    [*] --> Running: run() entered
    Running --> Running: dispatch AI + tools (FuturesUnordered)
    Running --> NeedsInteraction: idle + inputs/approvals/retryables
    Running --> Completed: all layers done
    Running --> Failed: terminal_error / NoRunnableNodesInLayer
    Running --> Cancelled: cancel_token
    NeedsInteraction --> Running: on_human_input / on_tool_decision / retry_node
    Completed --> [*]
    Failed --> [*]
    Cancelled --> [*]
EngineRunResult variantMeaning
NeedsInteraction { inputs, approvals, retryables }Batch pause; run not terminal
Completed(RunReport)All nodes finished; aggregated outputs + counters
Failed(RunError)Non-recoverable run failure
CancelledUser/system cancel via CancellationToken

Pause payloads:

StructFieldsWhen
EngineAwaitInputnode_id, label, context, is_initialExplicit NeedsUserInput request
EngineAwaitApprovalapproval_id, node_id, label, tool_callsBatch has requires_approval == true
EngineRetryableNodenode_id, label, error, interruptedTransient exhaustion, user interrupt, or recoverable node error

3.2 Per-node turn (harness vs executable batching) ​

Sources: ports/outbound.rs → AgentTurnOutcome; providers/src/mapping/mod.rs → all_tool_specs / resolve_tool_turn_outcome; interactive_engine/completion.rs

stateDiagram-v2
    [*] --> Ready: build_request (unified catalog)
    Ready --> AwaitingInput: NeedsUserInput / Message (conversational)
    Ready --> PendingTools: ToolCalls (executable only)
    PendingTools --> RunningTools: auto-allow OR approved
    RunningTools --> Ready: on_tool_results / queued live user message
    Ready --> Completed: AgentTurnOutcome#58;#58;Completed
    Ready --> Failed: non-retryable AgentError
    Ready --> Backoff: AgentError#58;#58;Transient
    Ready --> Ready: MixedToolTurn retry feedback
    Backoff --> Ready: retry_after elapsed
    Completed --> [*]
    Failed --> [*]

Unified tool catalog (every turn):

KindModel-facing tools
Harnessopenflow_submit_node_output, optional openflow_request_user_input, optional openflow_ask_user_question
ExecutableCatalog tools from registry (+ ToolPort::augment_request)

A batch holds exactly one harness tool or one or more executable tools. Mixing them produces AgentError::MixedToolTurn.

AgentTurnOutcome routing (completion.rs):

OutcomeEffect
CompletedStore NodeRunOutput, clear recovery state, advance layer when all siblings done
ToolCallsapply_tool_calls → approval policy → pending_tool_batches
NeedsUserInputConversational: pause (valid human-facing message or structured question). Autonomous: auto-continue nudge
MessageDirect chat (conversationMode): end turn and await next message. Workflow: auto-continue nudge (max streak)

Provider requests follow the same split: workflow nodes require a tool call; conversationMode nodes use automatic tool choice, so plain text is a valid outcome.

While a node is running model or tool work, ExecutionAction::ProvideInput goes to the engine's user-message queue. The engine appends the message to the node transcript, and the next AgentRequest includes it. Paused input, approval, and retry actions keep their own interaction handlers.

3.3 ToolCallStatus lifecycle ​

Source: crates/engine/src/tools/config.rs → ToolCallStatus

ToolCallStatus is used for UI and orchestration projection (events.rs). The engine loop does not use it.

stateDiagram-v2
    [*] --> Proposed: ToolCallProposed
    Proposed --> AwaitingApproval: ApprovalMode requires prompt
    Proposed --> Running: auto-allow
    AwaitingApproval --> Running: ToolApproved
    AwaitingApproval --> Blocked: ToolDenied / read_only blocked
    Running --> Completed: success
    Running --> Failed: error result
    Running --> Aborted: run Aborted
    Blocked --> [*]
    Completed --> [*]
    Failed --> [*]
    Aborted --> [*]

3.4 Orchestration session / RunStatus ​

Sources: run/persistence.rs → RunStatus; run/state.rs → WorkflowRunState, AgentStatus

stateDiagram-v2
    [*] --> Running: start_run / resume
    Running --> Paused: checkpoint (input / approval / retry)
    Running --> Stopped: user stop
    Running --> Completed: Finished
    Running --> Failed: NodeFailed / terminal error
    Paused --> Running: continue_run / resume_durable_run / submit_*
    Stopped --> Running: continue_run (in-session checkpoint)
    Completed --> [*]
    Failed --> [*]

AgentStatus (per-node, projected): Idle → Queued → Started → (RunningTool | AwaitingInput | AwaitingToolApproval) → (Completed | Failed | Interrupted | Stopped)

TraceStatus (timeline entries): Queued | Running | Paused | Failed | Stopped | Completed

Live flags on WorkflowRunState: active, awaiting_node_ids, pending_approvals, status_by_node, chat_logs, run_trace.

3.5 Subagent status ​

Source: engine/tools/config.rs → SubagentStatus; telemetry SubagentsDeclared / SubagentStarted / SubagentCompleted / SubagentFailed

stateDiagram-v2
    [*] --> Declared: openflow_declare_subagents
    Declared --> Active: openflow_call_subagent → SubagentStarted
    Active --> Completed: subagent turn machine Done
    Active --> Failed: SubagentFailed
    Completed --> [*]
    Failed --> [*]

Subagent turns run inside a parent CALL_SUBAGENT tool execution (subagent_runtime.rs) and are not peers of the nodes in a DAG layer.

4. Validation, pauses, and recovery ​

GateTriggerOwnerBlocks?Resume APIFailure modeSource
Workflow validationEmpty graph, duplicate ids, dangling edge, cycleengineYes (start)Fix workflowWorkflowValidationErrorgraph/validation.rs → validate_workflow
Provider / key readinessAny referenced provider lacks resolvable credentialsorchestrationYes (start)Settings / transient keyBackend error to UIsettings/provider.rs → resolve_provider_config
MCP setupEnabled server cannot connect, list tools, or register a toolorchestrationNoFix, test, or disable server in SettingsServer/tool skipped; system chat warningadapters/mcp/mod.rs → run/execution/drive/setup.rs
request_user_inputModel calls openflow_request_user_input with one free-text question or openflow_ask_user_question with valid structured questionsengineYes (pause)submit_user_inputInvalid request → nudge then failcompletion.rs, allow_user_input on AgentRequest
Tool approval (ApprovalMode)Write-tier tool under write / always_askengine policy; orch catalogYes (pause)submit_tool_approval → on_tool_decisionDeny → synthetic denied ToolResulttools/config.rs, completion.rs
read_only tool exposureWrite-tier tool not in catalogorchestrationYes (model can't call)Switch ApprovalModeTool unavailable to modeltool/registry.rs → is_read_only
Retryable node / RetryPolicyAgentError::Transient, interrupt, recoverable failengineYes (pause)retry_nodeExhaust → failed_nodes / run failcompletion.rs, RetryPolicy in WorkflowSettings
CancelUser stops an addressed runorchestration + tokenYes (terminal)New run / continueEngineRunResult::CancelledCancellationToken, stop_run(run_id)
Structured output / completion protocolInvalid openflow_submit_node_output JSONengineNo (nudge retry)Auto (≤3 nudges)Then failed_nodesAgentError::MalformedSubmitOutput
DAG layer readinessUpstream node lacks outputengineNo (wait)Auto when upstream completesN/Ais_node_blocked, gather_call_ai_actions
Checkpoint restoreUser stop / durable resumeorchestrationN/Acontinue_run / resume_durable_runHash / stale node mismatchcheckpoint.rs, run_checkpoint_store.rs
Callable agent snapshotUnknown agent id at run startengineYes (prep)Fix workflow/agentsPrep errorresolve_callable_agent_snapshots

Provider resolution order: node AgentNodeConfig.provider_id → workflow WorkflowSettings.provider_id → active settings provider. Run prep creates clients for every referenced provider. Requests never fail over across providers.

API key resolution order (resolve_api_key): transient input panel → stored ProviderProfile.api_key → provider env var. Bedrock uses AWS profile/region, not API keys.

5. Data flowing across seams ​

flowchart LR
    W["Workflow + Node + AgentNodeConfig"]
    NI["NodeInvocationContext"]
    AR["AgentRequest"]
    WIRE["Provider wire JSON/SSE"]
    ATO["AgentTurnOutcome"]
    TC["ToolCall / ToolResult"]
    RT["RunTelemetry"]
    DTO["WorkflowRunState"]

    W --> NI --> AR --> WIRE --> ATO
    ATO --> TC
    ATO --> RT
    TC --> RT
    RT --> DTO
SeamTypeImportant fields
Workflow → invocationNodeInvocationContextupstream outputs and handoff refs, entrypoint_text, shared_context, callable snapshots
→ AgentRequestbuild_agent_requestprovider_id, model, reasoning options, fast_mode, system_messages, task_prompt, input JSON, output_schema, tool_config (approval + structured-input capability), available_tools, allow_user_input, transcript
→ providerProviderRouter → AiPort::invoke_streamEffective provider, model id, reasoning options, independent fast-mode service tier, phase-specific available_tools (mapping/mod.rs → all_tool_specs)
← providerAgentTurnOutcomeCompleted.output, optional Completed.handoff, ToolCalls.tool_calls, control transitions
Tool executionToolPort::execute_batchToolBatchOutput → on_tool_results
→ UIRunTelemetry / ExecutionEventChatMessageDelta, NodeAwaitingInput, ToolApprovalRequested, NodeCompleted, Finished
Projectionapply_event_to_run_stateMutates status_by_node, chat_logs, pending_approvals, outputs, handoffs

Orchestration wraps the run-scoped provider router with AiInvocationAdapter. After a valid submit, the adapter writes the configured Markdown or JSON handoff under the run root, attaches its immutable run:// manifest, then emits completion and usage events.

6. Persistence map ​

DataLocationWriterNotes
App workflows{data_local}/openflow/workflows.jsonapp_workflow_storeUnassigned workflows only on save
Chats{data_local}/openflow/chats.jsonchat_storeChat identity, title, project/runtime config, timestamps, and durable run ID; separate from workflow catalog
Project workflows{project}/.flow/workflows/{id}.workflow.jsonproject_workflow_storeWins on ID collision at load
App-managed execution folders{data_local}/openflow/workspaces/{workflows|chats}/{id}workspace resolverIsolated cwd for runs without a project
Settings + API keys{data_local}/openflow/settings.jsonsettings_storePlaintext keys
Projects registry{data_local}/openflow/projects.jsonproject_storeBindings + assigned workflow ids
Saved agents (CallableAgent){data_local}/openflow/agents.jsonagent_storeSnapshotted at run start
Run record{run_root}/{run_id}/run.jsonRunCoordinatorRunRecord + workflow_snapshot
Checkpoints{run_root}/{run_id}/checkpoints/NNNN.jsonrun_checkpoint_storeEngine + WorkflowRunState projection
Artifacts{run_root}/{run_id}/artifacts/ArtifactStoreLarge tool outputs
Node handoffs{run_root}/{run_id}/handoffs/{node_id}/HANDOFF.md or HANDOFF.jsonHandoffStoreCanonical node result; SHA-256 manifest travels through run state and downstream input
In-session checkpointRunSession.engine_checkpointdrive lifecycleUser stop → Continue (no disk required)

Merge rule: WorkflowCatalog::load_all loads the app store, then overlays project-discovered workflows (project wins on the same WorkflowId).

7. Concurrency & cancellation ​

MechanismBehaviorSource
Top-level runsRunRegistry addresses sessions by run_id, permits one active run per workflow, and targets 64 in-process sessions by evicting inactive sessions first; active sessions are never evictedrun/registry.rs
Provider budgetAt most 8 active provider invocations; excess calls await a permitrun/resources.rs
Tool budgetAt most 16 executable tools across runs; excess calls await a permitrun/resources.rs, tool_port.rs
Same-cwd mutationOne mutation-capable run per canonical execution cwdrun/resources.rs, execution/mod.rs
Layer parallelismSame-layer ready nodes dispatch concurrently via FuturesUnorderedinteractive_engine/mod.rs
Completion orderingAI/tool completions applied serially as they finishin_flight.next() in run()
Cross-layerStrict: layer N+1 after all layer N outputscurrent_layer_complete
Tool batch parallelismToolConcurrency::Shared tools spawn in parallel inside batchtool_port.rs
Exclusive toolsSemaphore permit before spawntool/registry.rs, tool_port.rs
SubagentsNested in parent tool; not layer-parallelsubagent_runtime.rs
CancelCancellationToken checked while waiting for a mutation lease and during run()addressed stop_run(run_id)
Event coalescing~30ms independently per active rundesktop/run_event_bridge.rs
Blocking I/OFilesystem/subprocess on spawn_blockingorchestration adapters

Headless vs interactive: both call drive_interactive_workflow with the same InteractiveEngine. Headless (run_workflow_headless) uses scripted ExecutionAction queues instead of the UI. Without a coordinator, checkpoints are not durably persisted.

8. Worked scenarios ​

A. Linear 2-node readiness happy path ​

  1. UI: start_run with entrypoint: null.
  2. validate_workflow → layers [ [A], [B] ].
  3. InteractiveEngine::new → layer 0: A has all required inputs → gather_call_ai_actions → AiPort::invoke_stream.
  4. Completed(AgentTurnSuccess) → output stored → layer advances.
  5. B gets the upstream map in AgentRequest.input → completes → EngineRunResult::Completed(RunReport).
  6. RunTelemetry::Finished(report) → WorkflowRunState.active = false.

B. Node pauses for user input, resume ​

  1. A node with request_user_input: true starts when its upstream inputs are ready.
  2. The model calls openflow_request_user_input or openflow_ask_user_question → awaiting_nodes → run() returns NeedsInteraction { inputs: [EngineAwaitInput { ... }], ... }.
  3. Orchestration emits NodeAwaitingInput → UI composer enabled.
  4. User: submit_user_input accepts the text, removes that node's actionable structured request from WorkflowRunState, appends the user transcript message, and forwards to engine.on_human_input(node_id, text). Awaiting/status transitions still follow execution events.
  5. Next run() → AI invoke with the user message in the transcript.

C. Tool needs approval, approve, continue ​

  1. Model returns AgentTurnOutcome::ToolCalls with a write-tier tool under ApprovalMode::Write.
  2. apply_tool_calls sets requires_approval: true on the batch.
  3. NeedsInteraction { approvals: [EngineAwaitApproval] } → ToolApprovalRequested.
  4. User: submit_tool_approval(allow: true) → on_tool_decision clears the approval flag.
  5. take_ready_tool_batches → ToolPort::execute_batch inside run().
  6. on_tool_results → next model turn with the unified tool catalog.

D. Write tool blocked under read_only ​

  1. NodeToolConfig.approval_mode = ReadOnly.
  2. ToolRegistry exposes only read-tier tools (read, search, …); write tools omitted.
  3. The model cannot invoke bash / write. Unavailable tools never reach the ToolDecision path, even through a policy edge case.

E. Node fails / retry / exhaust ​

  1. Provider returns AgentError::Transient → backoff via WorkflowSettings.retry_policy.
  2. Retry streak exhausted → failed_nodes → EngineRetryableNode in NeedsInteraction.
  3. UI: retry_node → engine.retry_node clears failure state.
  4. Non-retryable error → Failed(RunError::NodeFailed) → run terminal.

F. Cancel mid-run ​

  1. UI: stop_run → cancel token + snapshot_and_abort.
  2. In-flight work sees the cancel at the next run() loop check → EngineRunResult::Cancelled.
  3. RunTelemetry::Aborted → projection marks nodes/tools stopped/aborted.

G. Subagent call ​

  1. Parent turn calls openflow_declare_subagents → SubagentsDeclared.
  2. openflow_call_subagent → SubagentStarted; nested state machine in subagent_runtime.rs.
  3. Subagent uses the unified catalog (allow_user_input: false) and tool batches until Done.
  4. SubagentCompleted / SubagentFailed → parent receives ToolResult.

H. Headless acceptance path ​

  1. Test calls run_workflow_headless(workflow, ...).
  2. Same wire_run + drive_interactive_workflow; manual inputs/approvals dequeued from script.
  3. Returns WorkflowRunSnapshot or an error; emits no Tauri run-state events.

9. Invariants & non-goals ​

Engine guarantees

  • Valid workflows have acyclic execution layers; validate_workflow is the gate.
  • A tool batch never mixes harness and executable tools (AgentError::MixedToolTurn).
  • NeedsInteraction is the only pause surface from run(); orchestration must not invent parallel pause semantics.
  • Node outputs propagate downstream only after Completed outcome stores structured output.

Orchestration must not

  • Reimplement approval rules (only project ToolCallStatus, filter catalogs).
  • Branch on concrete provider types (use Box<dyn AiPort>).
  • Construct InteractiveEngine outside run/execution/.

Providers must not

  • Decide tool approval, DAG advancement, or run lifecycle.
  • Offer a human-input harness when allow_user_input == false.
  • Persist workflow or session state.

Boundary violations to avoid: UI importing engine types directly; desktop calling orchestration internals; engine performing filesystem/HTTP I/O.

10. Appendix ​

A. Key file index ​

PathRole
crates/ui/src/api.tsIPC seam; start_run, submit_*, listenToRunState
crates/ui/src/context/appProvider/useRunSession.tsRun / continue / kickoff handlers
crates/desktop/src/commands/run.rsTauri command wiring
crates/desktop/src/run_event_bridge.rsRunTelemetry → run-state
crates/orchestration/src/backend/runs.rsAppBackend run entry
crates/orchestration/src/run/coordinator/mod.rsSession lifecycle
crates/orchestration/src/run/coordinator/session.rsprepare_workflow_run
crates/orchestration/src/run/prep.rsReasoning defaults, execution prep
crates/orchestration/src/run/execution/drive/mod.rsMain drive loop
crates/orchestration/src/run/execution/drive/interaction.rsPause/resume host
crates/orchestration/src/run/execution/events.rsapply_event_to_run_state
crates/orchestration/src/run/execution/tool_port.rsToolPort impl
crates/orchestration/src/run/execution/ai_adapter.rsStreaming + telemetry wrapper
crates/orchestration/src/run/execution/headless.rsAcceptance runner
crates/orchestration/src/workflow/catalog.rsWorkflow merge/split
crates/orchestration/src/settings/provider.rsKey resolution
crates/engine/src/execution/interactive_engine/mod.rsInteractiveEngine, EngineRunResult
crates/engine/src/execution/interactive_engine/completion.rsTurn outcome routing
crates/engine/src/execution/node_invocation.rsAgentRequest assembly
crates/engine/src/execution/subagent_runtime.rsSubagent builtins
crates/engine/src/graph/validation.rsGraph gates
crates/engine/src/ports/outbound.rsPort contracts
crates/engine/src/execution/telemetry.rsRunTelemetry
crates/providers/src/lib.rscreate_provider
crates/providers/src/client.rsAiClient: AiPort
crates/providers/src/mapping/mod.rsTool specs + outcome parsing

B. Enum cheat sheet ​

EnumVariantsFile
EngineRunResultNeedsInteraction, Completed, Failed, Cancelledinteractive_engine/mod.rs
AgentTurnOutcomeCompleted, ToolCalls, NeedsUserInput, Messageports/outbound.rs
AgentErrorTransient, Permanent, Failed, MalformedSubmitOutput, MixedToolTurn, Interruptedports/outbound.rs
ApprovalModeReadOnly, AlwaysAsk, Write, Yolotools/config.rs
ToolDecisionAutoAllow, Prompt, Deny (reserved)tools/config.rs
ToolCallStatusProposed, AwaitingApproval, Running, Completed, Blocked, Failed, Abortedtools/config.rs
SubagentStatusDeclared, Active, Completed, Failedtools/config.rs
RunStatusRunning, Paused, Stopped, Completed, Failedrun/persistence.rs
RunCheckpointReasonAwaitingInput, AwaitingToolApproval, AwaitingRetry, UserStopped, Completed, Failedrun/persistence.rs
AgentStatusIdle, Queued, Started, AwaitingInput, AwaitingToolApproval, RunningTool, Completed, Failed, Interrupted, Stoppedrun/state.rs
TraceStatusQueued, Running, Paused, Failed, Stopped, Completedrun/state.rs
WorkflowValidationErrorEmptyWorkflow, DuplicateNodeId, DuplicateEdgeId, MissingEndpoint, SelfEdge, Cycle, InternalConsistencygraph/validation.rs

RunTelemetry variants: NodeQueued, NodeStarted, ChatMessage, ChatMessageDelta, NodeAwaitingInput, ToolCallProposed, ToolApprovalRequested, ToolApproved, ToolDenied, ToolStarted, ToolRetrying, ToolUpdated, ToolCompleted, ToolArtifactCreated, FileChanged, EditBatchRecorded, NodeCompleted, NodeFailed, NodeInterrupted, NodeErrored, Finished, Aborted, Error, SubagentsDeclared, SubagentStarted, SubagentCompleted, SubagentFailed, PhaseTimed, UsageReported, AiInvokeFailed.

C. Ambiguities / dead paths / TODOs ​

ItemNotes
ToolDecision::DenyReserved in decision_from_mode; not emitted today.
Run entry pointsThe header Run button starts without a starter message (AppHeader.tsx). Runs can also start through ⌘/Ctrl+Enter or the chat kickoff composer (useRunSession.ts).
AiInvokeFailedEmitted but apply_event_to_run_state intentionally no-ops (incident hook).
Stale pause actionsWrong-node input/approval logged as "ignored …" Error event; skipped non-fatally.
ReadOnly approval matrixEngine decision_from_mode auto-allows all tiers; write blocking is enforced by orchestration tool catalog filtering.

Spot-check (2026-07-18): verified EngineRunResult variants (mod.rs:62–72), validate_workflow delegation (validation.rs:64–66), ApprovalMode + decision_from_mode (config.rs:69–77), RunStatus variants (persistence.rs:9–14), and RunTelemetry terminal variants (telemetry.rs:126–128).

Run preparation and dispatch ​

Runtime path ​

text
UI
  -> Desktop IPC
    -> Orchestration
      -> Engine
      -> Providers
      -> Tools and storage adapters
  1. The UI sends a start, continue, or resume command through the desktop IPC seam.
  2. Orchestration resolves settings, provider readiness, workflow storage, run root, and execution cwd.
  3. Orchestration creates the provider adapter through providers::create_provider.
  4. The engine validates and advances the workflow.
  5. Agent nodes assemble an AgentRequest: system prompt, upstream input, shared context, tools, and callable-agent snapshots.
  6. The provider returns model output, tool calls, or terminal output.
  7. Tool calls go through ToolPortImpl in orchestration and can pause for approval.
  8. Orchestration projects engine events into run state, trace entries, chat logs, and desktop events.
  9. Durable run data supports history and read-only replay. Paused, stopped, and failed runs can resume when their latest checkpoint is recoverable; completed workflow runs are replay-only. See Run persistence for the saved records and checkpoints.

Ports ​

Add a port only when a consumer is typed against its interface; otherwise call the concrete type directly. OpenFlow uses:

  • AiPort and AgentRequest: model invocation
  • ToolPort: tool and subagent execution
  • InteractiveEngine::on_human_input and InteractiveEngine::on_tool_decision: resuming interaction pauses
  • api.ts wrappers: UI-to-desktop calls

Released under the MIT License.