Skip to main content

Top-level structure & pipelines

A .flow file contains one or more workflows. This page covers one workflow's skeleton: declaration, resources, limits, types, pipeline and stages.

The workflow declaration

workflow deliver_change(input: ChangeRequest) -> Delivery {
// use / limits / type / pipeline, any order
pipeline delivery {
return { changes: [], review: { approved: true } }
}
}
  • The parameter must be named input, and there is exactly one (workflow-param-name)
  • The output type follows ->: the pipeline's return value is checked at compile time (workflow-return-mismatch), then again against the Schema at runtime
  • At most one limits and one pipeline; the pipeline is required

use: declare resources

use team "engineering-team" as engineering
use agent "secure-code-reviewer" as security

use declares logical resources — the string is a resource id pointing at no concrete product. Resolution happens at runtime (AgentRuntime.resolveAgent/resolveTeam); aliases are workflow-unique.

limits: budgets

limits {
concurrency: 4 // max concurrent agent calls (positive integer)
agent_runs: 12 // max total agent invocations for the workflow
duration: 30m // wall-clock cap (duration literal)
}

All three fields are optional; the effective value is the smaller of host policy ∩ workflow limits. Exceeding agent_runs raises LimitExceededError; exceeding duration raises WorkflowTimeoutError.

type: structured types

type WorkItem {
id: text
owner: member<engineering>
writes: path[]
acceptance: text[]
}

type Plan {
summary: text
work: WorkItem[1..6] // array, 1 to 6 items
}

Declarations may appear in any order with forward references allowed; recursion is not. Full rules in Type system.

pipeline and stages

The pipeline is a statement container; a stage is a named phase whose name is its result variable:

pipeline delivery {

stage plan -> Plan {
let result = agent(engineering.main) { /* ... */ expect Plan }
require result.work.count in 1..6 else fail "..."
return result
}

// ↓ explicit dependency declaration
stage execute after plan -> ChangeResult[] {
// plan is visible here
return parallel map plan.work as item { /* ... */ }
}

stage review after execute -> ReviewResult { /* ... */ }

return { changes: execute, review: review } // last statement must be return
}

The three dependency rules

RuleDiagnostic
A dependency must already be declaredunknown-stage
Dependencies must precede in source order (no forward deps)forward-dependency
A stage body may only read results of declared dependencies — even if an earlier stage already completedundeclared-dependency

Dependencies are a whitelist: with stage execute after plan, the body sees plan and nothing else. Every stage's input set is fully auditable from its signature.

Return placement

  • The pipeline's last statement must be return (missing-return), and return may only appear at the end (pipeline-return-position)
  • Same for stages (stage-missing-return)
  • A stage's return value must be assignable to the declared -> TypeRef (stage-return-mismatch)

V1 executes stages in source order; dependency metadata flows into the Flow IR (StageIR.dependencies) for a future DAG scheduler — declaring dependencies completely is leaving information for that scheduler.

Statements in pipeline / stage bodies

StatementGrammarConstraint
letlet name = exprbinds in current scope; duplicate names → duplicate-variable
requirerequire bool else fail "reason"condition must be bool; failure raises WorkflowAssertionError
emitemit progress { ... }progress events only, object-literal payload
ifif bool { ... } else { ... }condition must be bool; then/else are block scopes
returnreturn exprplacement as above

Stages cannot nest stages; pipelines cannot contain bare expressions.

Pattern: conditional early return

Inside a stage, combine an if-return with a trailing fallback:

stage route after audit -> text {
if audit.needs_manual_review {
return agent(approver) { task "..." expect text }
} else {
emit progress { decision: "direct" }
}
return agent(preparer) { task "..." expect text } // fallback
}

Next steps